Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions semcore/core/src/core-types/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ export namespace Intergalactic {
/** @private */
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace InternalTypings {
export type RemoveIndexSignature<T> = {
[K in keyof T as string extends K
? never
: number extends K
? never
: symbol extends K
? never
: K]: T[K];
};

type StripDefaultPrefix<K> = K extends `default${infer Rest}` ? Uncapitalize<Rest> : K;

export type ValidDefaultProps<DefaultProps, MergedProps> = {
Expand Down
2 changes: 1 addition & 1 deletion semcore/feedback-form/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"author": "UI-kit team <ui-kit-team@semrush.com>",
"license": "MIT",
"scripts": {
"build": "pnpm semcore-builder --source=js,ts && pnpm vite build"
"build": "pnpm semcore-builder && pnpm vite build"
},
"exports": {
"types": "./lib/types/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Box } from '@semcore/base-components';
import Button from '@semcore/button';
import type { Intergalactic } from '@semcore/core';
import { createComponent, Component, sstyled, Root } from '@semcore/core';
import { NoticeSmart } from '@semcore/notice';
import SpinContainer from '@semcore/spin-container';
Expand All @@ -9,9 +10,17 @@ import { Field, Form } from 'react-final-form';

import { FeedbackItem } from './component/feedback-item/FeedbackItem';
import { SubmitButton } from './component/submit-button/SubmitButton';
import type { NSFeedbackForm } from './FeedbackForm.type';
import style from './style/feedback-form.shadow.css';

class FeedbackForm extends Component {
class FeedbackForm extends Component<
Intergalactic.InternalTypings.InferComponentProps<NSFeedbackForm.Component>,
[],
{},
{},
{},
NSFeedbackForm.DefaultProps
> {
static displayName = 'FeedbackForm';
static style = style;
static FinalForm = {
Expand All @@ -24,14 +33,14 @@ class FeedbackForm extends Component {
};

static validate = {
description: (error) => (value = '') => {
description: (error: string) => (value = '') => {
const words = value.split(/\s+/);
const symbols = words.join(' ');
if (symbols.length < 10 || words.length < 3) {
return error;
}
},
email: (error) => (value = '') => {
email: (error: string) => (value = '') => {
if (!/.+@.+\..+/i.test(String(value).toLowerCase())) {
return error;
}
Expand Down Expand Up @@ -79,7 +88,7 @@ class FeedbackForm extends Component {
}
}

function Success(props) {
function Success(props: Intergalactic.InternalTypings.InferComponentProps<NSFeedbackForm.Success.Component>) {
const { Children, styles } = props;
const SSuccess = Root;
const SEmail = 'div';
Expand All @@ -96,24 +105,27 @@ function Success(props) {
// because it is used without a wrapper
Success.style = style;

function Cancel(props) {
function Cancel(props: Intergalactic.InternalTypings.InferComponentProps<NSFeedbackForm.Cancel.Component>) {
const { styles } = props;
const SCancel = Root;
return sstyled(styles)(<SCancel render={Button} type='reset' use='secondary' theme='muted' />);
}

function Notice(props) {
const { styles, theme = 'muted', use = 'secondary' } = props;
function Notice(props: Intergalactic.InternalTypings.InferComponentProps<NSFeedbackForm.Notice.Component>) {
const { styles, theme = 'muted' } = props;
const SNotice = Root;
return sstyled(styles)(<SNotice render={NoticeSmart} use:theme={theme} use:use={use} />);
return sstyled(styles)(<SNotice render={NoticeSmart} use:theme={theme} />);
Comment thread
ilyabrower marked this conversation as resolved.
}

/**
* FeedbackForm
*
* {@link https://developer.semrush.com/intergalactic/components/feedback-form/feedback-form-api/|API} | {@link https://developer.semrush.com/intergalactic/components/feedback-form/feedback-form-code/|Examples}
*/
export default createComponent(FeedbackForm, {
export default createComponent<
NSFeedbackForm.Component,
typeof FeedbackForm
>(FeedbackForm, {
Item: FeedbackItem,
Success,
Submit: SubmitButton,
Expand Down
63 changes: 63 additions & 0 deletions semcore/feedback-form/src/FeedbackForm.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { Box } from '@semcore/base-components';
import type Button from '@semcore/button';
import type { Intergalactic } from '@semcore/core';
import type { NoticeSmart } from '@semcore/notice';
import type { FormProps } from 'react-final-form';

import type { NSFeedbackFormFeedbackItem } from './component/feedback-item/FeedbackItem.type';
import type { NSFeedbackFormSubmitButton } from './component/submit-button/SubmitButton.type';

declare namespace NSFeedbackForm {
type Props = Intergalactic.InternalTypings.RemoveIndexSignature<FormProps> & {
/** The event is called when the form is submitted */
onSubmit: (values: any, form: any, callback?: (errors?: {}) => void) => {} | Promise<{}> | void;
/**
* The property is in charge of the spinner showing
* */
loading?: boolean;
/**
* Color of container spinner; you can use your own color
*/
background?: string;
/** Spinner theme. There are several default themes or you can use your own color
* @default dark
**/
theme?: 'dark' | 'invert' | string;
};
type DefaultProps = {
onSubmit: () => void;
};

namespace Item {
type Component = NSFeedbackFormFeedbackItem.Component;
}

namespace Success {
type Component = typeof Box;
}

namespace Submit {
type Component = NSFeedbackFormSubmitButton.Component;
}

namespace Cancel {
type Component = typeof Button;
}

namespace Notice {
type Component = typeof NoticeSmart;
}

type Component = Intergalactic.Component<'form', Props> & {
Item: Item.Component;
Success: Success.Component;
Submit: Submit.Component;
Cancel: Cancel.Component;
Notice: Notice.Component;
};
}

/** @deprecated It will be removed in v18. */
export type FeedbackFormProps = NSFeedbackForm.Props;

export type { NSFeedbackForm };
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import Checkbox from '@semcore/checkbox';
import { createComponent, Component, Root, sstyled } from '@semcore/core';
import React from 'react';

import type { NSFeedbackFormCheckboxButton } from './CheckboxButton.type';
import style from '../../style/checkbox-button.shadow.css';
import type { FeedbackRatingCheckboxComponent, FeedbackRatingCheckboxProps } from '../feedback-rating/FeedbackRating.type';

class CheckboxButtonRoot extends Component<FeedbackRatingCheckboxProps> {
class CheckboxButtonRoot extends Component<NSFeedbackFormCheckboxButton.Props> {
static style = style;

checkboxRef = React.createRef<HTMLInputElement>();
Expand Down Expand Up @@ -37,7 +37,7 @@ class CheckboxButtonRoot extends Component<FeedbackRatingCheckboxProps> {
}

const CheckboxButton = createComponent<
FeedbackRatingCheckboxComponent,
NSFeedbackFormCheckboxButton.Component,
typeof CheckboxButtonRoot
>(CheckboxButtonRoot);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { NSCheckbox } from '@semcore/checkbox';
import type { Intergalactic } from '@semcore/core';
import type React from 'react';

declare namespace NSFeedbackFormCheckboxButton {
type Props = Omit<NSCheckbox.Props, 'label'> & {
focused: boolean;
label: React.ReactNode;
};

type Component = Intergalactic.Component<'div', Props>;
}

export type { NSFeedbackFormCheckboxButton };
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Intergalactic } from '@semcore/core';
import { assignProps } from '@semcore/core';
import pick from '@semcore/core/lib/utils/pick';
import propsForElement from '@semcore/core/lib/utils/propsForElement';
Expand All @@ -6,6 +7,8 @@ import Tooltip from '@semcore/tooltip';
import React from 'react';
import { Field } from 'react-final-form';

import type { NSFeedbackFormFeedbackItem } from './FeedbackItem.type';

const deafultTooltipPropsList = [
'title',
'theme',
Expand All @@ -32,8 +35,8 @@ export function FeedbackItem({
uid,
tooltipProps: tooltipPropsList = deafultTooltipPropsList,
...props
}: any) {
const tooltipProps = pick(props, tooltipPropsList);
}: Intergalactic.InternalTypings.InferComponentProps<NSFeedbackFormFeedbackItem.Component> & { tag: Intergalactic.InternalTypings.ComponentTag }) {
const tooltipProps = pick(props, tooltipPropsList as (keyof typeof props)[]);
const lastErrorRef = React.useRef(undefined);

return (
Expand All @@ -57,6 +60,7 @@ export function FeedbackItem({
if (meta?.error) lastErrorRef.current = meta.error;

return (
// @ts-ignore
<Tooltip
visible={errorState && meta.active}
theme='warning'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Intergalactic } from '@semcore/core';
import type { TooltipProps } from '@semcore/tooltip';
import type { FieldProps, FieldInputProps, FieldMetaState } from 'react-final-form';

import type { NSFeedbackForm } from '../../FeedbackForm.type';

declare namespace NSFeedbackFormFeedbackItem {
type Props = Intergalactic.InternalTypings.RemoveIndexSignature<FieldProps<any, any>> & TooltipProps & {
/**
* Allows to override which passed props will be passed to the Tooltip component.
*/
tooltipProps?: string[];
validateOnBlur?: NSFeedbackForm.Props['validateOnBlur'];
};
type Ctx = {
input: FieldInputProps<any> & { state: 'normal' | 'invalid' };
meta: FieldMetaState<any>;
};

type Component = Intergalactic.Component<'div', Props, Ctx>;
}

export type { NSFeedbackFormFeedbackItem };
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Box, Flex } from '@semcore/base-components';
import type Button from '@semcore/button';
import type Checkbox from '@semcore/checkbox';
import { createComponent, Component, sstyled, Root } from '@semcore/core';
import type { Intergalactic } from '@semcore/core';
import type { WithI18nEnhanceProps } from '@semcore/core/lib/utils/enhances/i18nEnhance';
import i18nEnhance from '@semcore/core/lib/utils/enhances/i18nEnhance';
import uniqueIDEnhancement from '@semcore/core/lib/utils/uniqueID';
import CheckM from '@semcore/icon/Check/m';
Expand All @@ -20,13 +19,7 @@ import createFocusDecorator from 'final-form-focus';
import React, { type ReactElement } from 'react';
import { Field, Form } from 'react-final-form';

import type {
FeedbackRatingCheckboxProps,
FeedbackRatingItemProps,
FeedbackRatingProps,
FormConfigItem,
FeedbackRatingDefaultProps,
} from './FeedbackRating.type';
import type { NSFeedbackFormFeedbackRating } from './FeedbackRating.type';
import style from '../../style/feedback-rating.shadow.css';
import { localizedMessages } from '../../translations/__intergalactic-dynamic-locales';
import CheckboxButton from '../checkbox-button/CheckboxButton';
Expand All @@ -39,19 +32,19 @@ type State = {
};

class FeedbackRatingRoot extends Component<
FeedbackRatingProps,
Intergalactic.InternalTypings.InferComponentProps<NSFeedbackFormFeedbackRating.Component>,
typeof FeedbackRatingRoot.enhance,
{},
{},
WithI18nEnhanceProps,
State,
FeedbackRatingDefaultProps
NSFeedbackFormFeedbackRating.DefaultProps
> {
static displayName = 'FeedbackRatingForm';
static style = style;

static enhance = [i18nEnhance(localizedMessages), uniqueIDEnhancement()] as const;

static defaultProps: FeedbackRatingDefaultProps = {
static defaultProps: NSFeedbackFormFeedbackRating.DefaultProps = {
onSubmit: () => {},
i18n: localizedMessages,
locale: 'en',
Expand Down Expand Up @@ -122,7 +115,7 @@ class FeedbackRatingRoot extends Component<
fn(e);
};

componentDidUpdate(prevProps: Readonly<FeedbackRatingProps>) {
componentDidUpdate(prevProps: typeof this.asProps) {
const { status, getI18nText } = this.asProps;

if (prevProps.status !== status) {
Expand All @@ -144,7 +137,7 @@ class FeedbackRatingRoot extends Component<
}
}

renderCheckbox = (config: FormConfigItem, index: number) => {
renderCheckbox = (config: NSFeedbackFormFeedbackRating.FormConfigItem, index: number) => {
const initialValue = this.props.initialValues[config.key];

return (
Expand All @@ -154,15 +147,15 @@ class FeedbackRatingRoot extends Component<
{...input}
id={config.key}
label={config.label}
onChange={(_checked, e) => input.onChange(e)}
onChange={(_: boolean, e?: React.SyntheticEvent<HTMLInputElement>) => input.onChange(e)}
focused={index === 0}
/>
)}
</Field>
);
};

renderTextField = (config: FormConfigItem) => {
renderTextField = (config: NSFeedbackFormFeedbackRating.FormConfigItem) => {
const initialValue = this.props.initialValues[config.key];

const label =
Expand Down Expand Up @@ -369,8 +362,6 @@ class FeedbackRatingRoot extends Component<
</SemcoreNotice.Label>
<SemcoreNotice.Content>
{getI18nText('errorMessage', {
// todo: Brauer Ilia - think how to fix type
// @ts-ignore
email: (
<Link href={`mailto:${errorFeedbackEmail}`}>
{errorFeedbackEmail}
Expand Down Expand Up @@ -401,29 +392,23 @@ class FeedbackRatingRoot extends Component<
}
}

function Header(props: any) {
function Header(
props: Intergalactic.InternalTypings.InferChildComponentProps<NSFeedbackFormFeedbackRating.Header.Component, typeof FeedbackRatingRoot, 'Header'>,
) {
const { styles } = props;
const SHeader = Root;
return sstyled(styles)(
<SHeader render={Modal.Title} />,
);
}

type FeedbackRatingComponent = Intergalactic.Component<'form', FeedbackRatingProps, {}, typeof FeedbackRatingRoot.enhance> & {
validate: typeof FeedbackRatingRoot.validate;
Item: Intergalactic.Component<'div', FeedbackRatingItemProps>;
Submit: typeof Button;
Checkbox: Intergalactic.Component<typeof Checkbox, FeedbackRatingCheckboxProps>;
Header: typeof Text;
};

/**
* FeedbackRating
*
* {@link https://developer.semrush.com/intergalactic/components/feedback-form/feedback-form-api#feedbackform-feedbackrating|API} | {@link https://developer.semrush.com/intergalactic/components/feedback-form/feedback-form-code/|Examples}
*/
const FeedbackRating = createComponent<
FeedbackRatingComponent,
NSFeedbackFormFeedbackRating.Component,
typeof FeedbackRatingRoot
>(FeedbackRatingRoot, {
Header,
Expand Down
Loading
Loading