From 9504631da837e78cf66c84286d5e7e98c2009148 Mon Sep 17 00:00:00 2001 From: Carlos Cortizas <97907068+CarlosCortizasCT@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:23:05 +0100 Subject: [PATCH] [FEC-139] Refactor React components "defaultProps" management (#3676) * refactor(application-components): update components default props management * chore: add changeset * refactor: apply review feedback --- .changeset/four-zebras-arrive.md | 36 +++++++++ .../custom-form-detail-page.tsx | 14 ++-- .../form-detail-page/form-detail-page.tsx | 14 ++-- .../tabular-detail-page.tsx | 14 ++-- .../confirmation-dialog.tsx | 22 +++-- .../dialogs/form-dialog/form-dialog.tsx | 20 +++-- .../dialogs/internals/dialog-container.tsx | 25 +++--- .../dialogs/internals/dialog-footer.tsx | 30 +++---- .../src/components/drawer/drawer.tsx | 35 ++++---- .../internals/default-form-buttons.tsx | 81 ++++++++----------- .../internals/page-header-title.tsx | 32 +++----- .../src/components/internals/page-top-bar.tsx | 22 +++-- .../custom-form-main-page.tsx | 19 ++--- .../form-main-page/form-main-page.tsx | 12 ++- .../tabular-main-page/tabular-main-page.tsx | 14 ++-- .../form-modal-page/form-modal-page.tsx | 11 +-- .../modal-pages/internals/modal-page.tsx | 38 +++++---- .../tabular-modal-page/tabular-modal-page.tsx | 11 +-- .../modal-pages/utils/modal-page-top-bar.tsx | 28 +++---- .../page-content-wide/page-content-wide.tsx | 27 +++---- .../public-page-layout/public-page-layout.tsx | 19 +++-- .../src/components/tab-header/tab-header.tsx | 21 +++-- .../application-page-title.tsx | 15 ++-- .../inject-reducers/inject-reducers.tsx | 13 +-- .../src/components/navbar/menu-items.tsx | 32 +++----- .../src/components/navbar/navbar-skeleton.tsx | 20 +++-- .../src/components/authorized/authorized.tsx | 11 ++- .../components/notification/notification.tsx | 16 ++-- .../src/components/notifier/notifier.tsx | 15 ++-- .../sdk/src/components/sdk-get/sdk-get.tsx | 9 ++- 30 files changed, 309 insertions(+), 367 deletions(-) create mode 100644 .changeset/four-zebras-arrive.md diff --git a/.changeset/four-zebras-arrive.md b/.changeset/four-zebras-arrive.md new file mode 100644 index 0000000000..33778c5cda --- /dev/null +++ b/.changeset/four-zebras-arrive.md @@ -0,0 +1,36 @@ +--- +'@commercetools-frontend/application-components': patch +'@commercetools-frontend/react-notifications': patch +'@commercetools-frontend/application-shell': patch +'@commercetools-frontend/permissions': patch +'@commercetools-frontend/sdk': patch +--- + +As part of the preparations for the upcoming update to the newest React version, we have updated how we manage components default properties as our current implementation will no longer be supported ([reference](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-proptypes-and-defaultprops)). + +```ts +// BEFORE +type TMyComponentProps = { + message: string; + size: string; +} + +function MyComponent(props: TMyComponentProps) { + ... +} + +MyComponent.defaultProps = { + size: 'big' +} + + +// AFTER +type TMyComponentProps = { + message: string; + size?: string; // <--- Note this property is now defined as optional +} + +function MyComponent({ size = 'big', ...props }: TMyComponentProps) { + ... +} +``` diff --git a/packages/application-components/src/components/detail-pages/custom-form-detail-page/custom-form-detail-page.tsx b/packages/application-components/src/components/detail-pages/custom-form-detail-page/custom-form-detail-page.tsx index 087d5eb300..c011332d80 100644 --- a/packages/application-components/src/components/detail-pages/custom-form-detail-page/custom-form-detail-page.tsx +++ b/packages/application-components/src/components/detail-pages/custom-form-detail-page/custom-form-detail-page.tsx @@ -83,11 +83,10 @@ type CustomFormDetailPageProps = { ) => void; }; -const defaultProps: Pick = { - hideControls: false, -}; - -const CustomFormDetailPage = (props: CustomFormDetailPageProps) => { +const CustomFormDetailPage = ({ + hideControls = false, + ...props +}: CustomFormDetailPageProps) => { warning( props.title !== undefined || props.customTitleRow !== undefined, 'DetailPage: one of either `title` or `customTitleRow` is required but both their values are `undefined`' @@ -110,11 +109,11 @@ const CustomFormDetailPage = (props: CustomFormDetailPageProps) => { )} - {!props.hideControls && props.formControls && ( + {!hideControls && props.formControls && ( {props.formControls} @@ -127,7 +126,6 @@ const CustomFormDetailPage = (props: CustomFormDetailPageProps) => { ); }; CustomFormDetailPage.displayName = 'CustomFormDetailPage'; -CustomFormDetailPage.defaultProps = defaultProps; // Static export of pre-configured page header title component to easily // use as part of a custom title row CustomFormDetailPage.PageHeaderTitle = PageHeaderTitle; diff --git a/packages/application-components/src/components/detail-pages/form-detail-page/form-detail-page.tsx b/packages/application-components/src/components/detail-pages/form-detail-page/form-detail-page.tsx index b54b2db0c8..b166f97311 100644 --- a/packages/application-components/src/components/detail-pages/form-detail-page/form-detail-page.tsx +++ b/packages/application-components/src/components/detail-pages/form-detail-page/form-detail-page.tsx @@ -87,18 +87,17 @@ type FormDetailPageProps = { /** * Hides the form controls. */ - hideControls: boolean; + hideControls?: boolean; /** * The Icon for the secondary button label */ iconLeftSecondaryButton?: ReactElement; }; -const defaultProps: Pick = { - hideControls: false, -}; - -const FormDetailPage = (props: FormDetailPageProps) => ( +const FormDetailPage = ({ + hideControls = false, + ...props +}: FormDetailPageProps) => ( ( customViewLocatorCode={props.customViewLocatorCode} previousPathLabel={props.previousPathLabel} onPreviousPathClick={props.onPreviousPathClick} - hideControls={props.hideControls} + hideControls={hideControls} formControls={ <> ( ); FormDetailPage.displayName = 'FormDetailPage'; -FormDetailPage.defaultProps = defaultProps; // This is a convenience proxy export to expose pre-defined Intl messages defined in the `@commercetools-frontend/i18n` package. // The Intl messages can be used for button labels. // Static export of pre-configured page header title component to easily diff --git a/packages/application-components/src/components/detail-pages/tabular-detail-page/tabular-detail-page.tsx b/packages/application-components/src/components/detail-pages/tabular-detail-page/tabular-detail-page.tsx index af1115ec4f..f30e9131b7 100644 --- a/packages/application-components/src/components/detail-pages/tabular-detail-page/tabular-detail-page.tsx +++ b/packages/application-components/src/components/detail-pages/tabular-detail-page/tabular-detail-page.tsx @@ -59,7 +59,7 @@ type TTabularDetailPageProps = { /** * Determines if the form controls should be rendered. */ - hideControls: boolean; + hideControls?: boolean; /** * These codes are used to configure which Custom Views are available for every tab. */ @@ -78,11 +78,10 @@ type TTabularDetailPageProps = { ) => void; }; -const defaultProps: Pick = { - hideControls: false, -}; - -const TabularDetailPage = (props: TTabularDetailPageProps) => { +const TabularDetailPage = ({ + hideControls = false, + ...props +}: TTabularDetailPageProps) => { const { currentCustomViewLocatorCode } = useCustomViewLocatorSelector( props.customViewLocatorCodes ); @@ -111,7 +110,7 @@ const TabularDetailPage = (props: TTabularDetailPageProps) => { tabControls={props.tabControls} formControls={ - {!props.hideControls && props.formControls && ( + {!hideControls && props.formControls && ( {props.formControls} @@ -131,7 +130,6 @@ const TabularDetailPage = (props: TTabularDetailPageProps) => { ); }; TabularDetailPage.displayName = 'TabularDetailPage'; -TabularDetailPage.defaultProps = defaultProps; // Static export of pre-configured form control buttons to easily re-use // them in the custom controls. TabularDetailPage.FormPrimaryButton = FormPrimaryButton; diff --git a/packages/application-components/src/components/dialogs/confirmation-dialog/confirmation-dialog.tsx b/packages/application-components/src/components/dialogs/confirmation-dialog/confirmation-dialog.tsx index 7cfa0508b3..8bf5d10fc6 100644 --- a/packages/application-components/src/components/dialogs/confirmation-dialog/confirmation-dialog.tsx +++ b/packages/application-components/src/components/dialogs/confirmation-dialog/confirmation-dialog.tsx @@ -22,8 +22,8 @@ export type TConfirmationDialogProps = { size?: 'm' | 'l' | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 'scale'; zIndex?: number; children: ReactNode; - labelSecondary: Label; - labelPrimary: Label; + labelSecondary?: Label; + labelPrimary?: Label; isPrimaryButtonDisabled?: boolean; onCancel: (event: SyntheticEvent) => void; onConfirm: (event: SyntheticEvent) => void; @@ -31,15 +31,12 @@ export type TConfirmationDialogProps = { dataAttributesPrimaryButton?: { [key: string]: string }; getParentSelector?: () => HTMLElement; }; -const defaultProps: Pick< - TConfirmationDialogProps, - 'labelSecondary' | 'labelPrimary' -> = { - labelSecondary: sharedMessages.cancel, - labelPrimary: sharedMessages.confirm, -}; -const ConfirmationDialog = (props: TConfirmationDialogProps) => ( +const ConfirmationDialog = ({ + labelSecondary = sharedMessages.cancel, + labelPrimary = sharedMessages.confirm, + ...props +}: TConfirmationDialogProps) => ( ( {props.children} ( ); ConfirmationDialog.displayName = 'ConfirmationDialog'; -ConfirmationDialog.defaultProps = defaultProps; // This is a convenience proxy export to expose pre-defined Intl messages defined in the `@commercetools-frontend/i18n` package. // The Intl messages can be used for button labels. ConfirmationDialog.Intl = sharedMessages; diff --git a/packages/application-components/src/components/dialogs/form-dialog/form-dialog.tsx b/packages/application-components/src/components/dialogs/form-dialog/form-dialog.tsx index 9194b253c4..800ef3447b 100644 --- a/packages/application-components/src/components/dialogs/form-dialog/form-dialog.tsx +++ b/packages/application-components/src/components/dialogs/form-dialog/form-dialog.tsx @@ -22,8 +22,8 @@ export type TFormDialogProps = { size?: 'm' | 'l' | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 'scale'; zIndex?: number; children: ReactNode; - labelSecondary: Label; - labelPrimary: Label; + labelSecondary?: Label; + labelPrimary?: Label; isPrimaryButtonDisabled?: boolean; onSecondaryButtonClick: (event: SyntheticEvent) => void; onPrimaryButtonClick: (event: SyntheticEvent) => void; @@ -33,13 +33,12 @@ export type TFormDialogProps = { iconLeftSecondaryButton?: ReactElement; footerContent?: ReactNode; }; -const defaultProps: Pick = - { - labelSecondary: sharedMessages.cancel, - labelPrimary: sharedMessages.save, - }; -const FormDialog = (props: TFormDialogProps) => ( +const FormDialog = ({ + labelSecondary = sharedMessages.cancel, + labelPrimary = sharedMessages.save, + ...props +}: TFormDialogProps) => ( ( {props.children} ( ); FormDialog.displayName = 'FormDialog'; -FormDialog.defaultProps = defaultProps; // This is a convenience proxy export to expose pre-defined Intl messages defined in the `@commercetools-frontend/i18n` package. // The Intl messages can be used for button labels. FormDialog.Intl = sharedMessages; diff --git a/packages/application-components/src/components/dialogs/internals/dialog-container.tsx b/packages/application-components/src/components/dialogs/internals/dialog-container.tsx index 414eaba75f..5da7770a97 100644 --- a/packages/application-components/src/components/dialogs/internals/dialog-container.tsx +++ b/packages/application-components/src/components/dialogs/internals/dialog-container.tsx @@ -42,20 +42,12 @@ const getOverlayElement: ModalProps['overlayElement'] = ( type Props = { isOpen: boolean; onClose?: (event: SyntheticEvent) => void; - size: 'm' | 'l' | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 'scale'; + size?: 'm' | 'l' | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 'scale'; zIndex?: number; title: ReactNode; 'aria-label'?: string; children: ReactNode; - getParentSelector: typeof getDefaultParentSelector; -}; -const defaultProps: Pick = { - // TODO: t-shirt sizes are deprecated but we need to keep using them for - // backwards compatibility and to help with styling migration - // After the migration is done, we should change this default value to 13. - // t-shirt sizes then can be removed in a next breaking change release - size: 'l', - getParentSelector: getDefaultParentSelector, + getParentSelector?: typeof getDefaultParentSelector; }; type GridAreaProps = { @@ -65,7 +57,11 @@ const GridArea = styled.div` grid-area: ${(props) => props.name}; `; -const DialogContainer = (props: Props) => { +const DialogContainer = ({ + size = 13, + getParentSelector = getDefaultParentSelector, + ...props +}: Props) => { useWarning( typeof props.title === 'string' || (typeof props.title !== 'string' && Boolean(props['aria-label'])), @@ -81,12 +77,12 @@ const DialogContainer = (props: Props) => { shouldCloseOnOverlayClick={Boolean(props.onClose)} shouldCloseOnEsc={Boolean(props.onClose)} overlayElement={getOverlayElement} - overlayClassName={makeClassName(getOverlayStyles(props))} - className={makeClassName(getModalContentStyles(props))} + overlayClassName={makeClassName(getOverlayStyles({ size, ...props }))} + className={makeClassName(getModalContentStyles({ size, ...props }))} contentLabel={ typeof props.title === 'string' ? props.title : props['aria-label'] } - parentSelector={props.getParentSelector} + parentSelector={getParentSelector} ariaHideApp={false} > @@ -140,6 +136,5 @@ const DialogContainer = (props: Props) => { ); }; DialogContainer.displayName = 'DialogContainer'; -DialogContainer.defaultProps = defaultProps; export default DialogContainer; diff --git a/packages/application-components/src/components/dialogs/internals/dialog-footer.tsx b/packages/application-components/src/components/dialogs/internals/dialog-footer.tsx index 87b46bd327..b92a70dad5 100644 --- a/packages/application-components/src/components/dialogs/internals/dialog-footer.tsx +++ b/packages/application-components/src/components/dialogs/internals/dialog-footer.tsx @@ -21,28 +21,23 @@ type Props = { labelPrimary: Label; onCancel: (event: SyntheticEvent) => void; onConfirm: (event: SyntheticEvent) => void; - isPrimaryButtonDisabled: boolean; - dataAttributesPrimaryButton: { [key: string]: string }; - dataAttributesSecondaryButton: { [key: string]: string }; + isPrimaryButtonDisabled?: boolean; + dataAttributesPrimaryButton?: { [key: string]: string }; + dataAttributesSecondaryButton?: { [key: string]: string }; children?: never; iconLeftSecondaryButton?: ReactElement; footerContent?: ReactNode; }; -const defaultProps: Pick< - Props, - | 'isPrimaryButtonDisabled' - | 'dataAttributesPrimaryButton' - | 'dataAttributesSecondaryButton' -> = { - isPrimaryButtonDisabled: false, - dataAttributesPrimaryButton: {}, - dataAttributesSecondaryButton: {}, -}; const getFormattedLabel = (label: Label, intl: IntlShape) => typeof label === 'string' ? label : intl.formatMessage(label); -const DialogFooter = (props: Props) => { +const DialogFooter = ({ + isPrimaryButtonDisabled = false, + dataAttributesPrimaryButton = {}, + dataAttributesSecondaryButton = {}, + ...props +}: Props) => { const intl = useIntl(); return (
{ label={getFormattedLabel(props.labelSecondary, intl)} onClick={props.onCancel} iconLeft={props.iconLeftSecondaryButton} - {...filterDataAttributes(props.dataAttributesSecondaryButton)} + {...filterDataAttributes(dataAttributesSecondaryButton)} /> @@ -75,6 +70,5 @@ const DialogFooter = (props: Props) => { ); }; DialogFooter.displayName = 'DialogFooter'; -DialogFooter.defaultProps = defaultProps; export default DialogFooter; diff --git a/packages/application-components/src/components/drawer/drawer.tsx b/packages/application-components/src/components/drawer/drawer.tsx index 2c979c65fd..9e0d307475 100644 --- a/packages/application-components/src/components/drawer/drawer.tsx +++ b/packages/application-components/src/components/drawer/drawer.tsx @@ -27,7 +27,7 @@ type Label = string | MessageDescriptor; type TDrawerSize = 10 | 20 | 30; type TDrawerProps = { - size: TDrawerSize; + size?: TDrawerSize; isOpen: boolean; onClose?: (event: SyntheticEvent) => void; children: ReactNode; @@ -50,21 +50,11 @@ type TDrawerProps = { dataAttributesSecondaryButton?: { [key: string]: string }; labelPrimaryButton?: Label; labelSecondaryButton?: Label; - onPrimaryButtonClick: (event: SyntheticEvent) => void; - onSecondaryButtonClick: (event: SyntheticEvent) => void; + onPrimaryButtonClick?: (event: SyntheticEvent) => void; + onSecondaryButtonClick?: (event: SyntheticEvent) => void; iconLeftSecondaryButton?: ReactElement; }; -const defaultProps: Pick< - TDrawerProps, - 'size' | 'hideControls' | 'onPrimaryButtonClick' | 'onSecondaryButtonClick' -> = { - size: 10, - hideControls: false, - onPrimaryButtonClick: () => {}, - onSecondaryButtonClick: () => {}, -}; - const ContentWrapper = styled.div` flex: 1; flex-basis: 0; @@ -77,7 +67,13 @@ const HeaderWrapper = styled.div` border-bottom: 1px solid ${uiKitDesignTokens.colorNeutral90}; `; -function Drawer(props: TDrawerProps) { +function Drawer({ + size = 10, + hideControls = false, + onPrimaryButtonClick = () => {}, + onSecondaryButtonClick = () => {}, + ...props +}: TDrawerProps) { const intl = useIntl(); return ( - {!props.hideControls && props.formControls && props.formControls} + {!hideControls && props.formControls && props.formControls} - {!props.hideControls && !props.formControls && ( + {!hideControls && !props.formControls && ( <> @@ -147,7 +143,6 @@ function Drawer(props: TDrawerProps) { } Drawer.displayName = 'Drawer'; -Drawer.defaultProps = defaultProps; // Static export of pre-configured form control buttons to easily re-use // them in the custom controls. Drawer.FormPrimaryButton = FormPrimaryButton; diff --git a/packages/application-components/src/components/internals/default-form-buttons.tsx b/packages/application-components/src/components/internals/default-form-buttons.tsx index f2c71052db..e98f214c20 100644 --- a/packages/application-components/src/components/internals/default-form-buttons.tsx +++ b/packages/application-components/src/components/internals/default-form-buttons.tsx @@ -17,22 +17,13 @@ type MessageDescriptor = { }; type Label = string | MessageDescriptor; type Props = { - label: Label; + label?: Label; onClick: (event: SyntheticEvent) => void; - isDisabled: boolean; - dataAttributes: { [key: string]: string }; + isDisabled?: boolean; + dataAttributes?: { [key: string]: string }; children?: never; }; -const primaryDefaultProps: Pick< - Props, - 'label' | 'isDisabled' | 'dataAttributes' -> = { - label: sharedMessages.confirm, - isDisabled: false, - dataAttributes: {}, -}; - const useFormattedLabel = (label: Label) => { const intl = useIntl(); @@ -43,77 +34,71 @@ type PrimaryButtonProps = { iconLeft?: ReactElement; } & Props; -const FormPrimaryButton = (props: PrimaryButtonProps) => { - const label = useFormattedLabel(props.label); +const FormPrimaryButton = ({ + label = sharedMessages.confirm, + isDisabled = false, + dataAttributes = {}, + ...props +}: PrimaryButtonProps) => { + const _label = useFormattedLabel(label); return ( ); }; FormPrimaryButton.displayName = 'FormPrimaryButton'; -FormPrimaryButton.defaultProps = primaryDefaultProps; type SecondaryButtonProps = { iconLeft?: ReactElement; } & Props; -const secondaryDefaultProps: Pick< - SecondaryButtonProps, - 'label' | 'isDisabled' | 'dataAttributes' -> = { - label: sharedMessages.cancel, - isDisabled: false, - dataAttributes: {}, -}; - -const FormSecondaryButton = (props: SecondaryButtonProps) => { - const label = useFormattedLabel(props.label); +const FormSecondaryButton = ({ + label = sharedMessages.cancel, + isDisabled = false, + dataAttributes = {}, + ...props +}: SecondaryButtonProps) => { + const _label = useFormattedLabel(label); return ( ); }; FormSecondaryButton.displayName = 'FormSecondaryButton'; -FormSecondaryButton.defaultProps = secondaryDefaultProps; - -const deleteDefaultProps: Pick< - Props, - 'label' | 'isDisabled' | 'dataAttributes' -> = { - label: sharedMessages.delete, - isDisabled: false, - dataAttributes: {}, -}; -const FormDeleteButton = (props: Props) => { - const label = useFormattedLabel(props.label); +const FormDeleteButton = ({ + label = sharedMessages.delete, + isDisabled = false, + dataAttributes = {}, + ...props +}: Props) => { + const _label = useFormattedLabel(label); return ( } - label={label} + label={_label} onClick={props.onClick} - isDisabled={props.isDisabled} - {...filterDataAttributes(props.dataAttributes)} + isDisabled={isDisabled} + {...filterDataAttributes(dataAttributes)} /> ); }; FormDeleteButton.displayName = 'FormDeleteButton'; -FormDeleteButton.defaultProps = deleteDefaultProps; export { FormPrimaryButton, FormSecondaryButton, FormDeleteButton }; diff --git a/packages/application-components/src/components/internals/page-header-title.tsx b/packages/application-components/src/components/internals/page-header-title.tsx index 08f40af7d2..b265d4420a 100644 --- a/packages/application-components/src/components/internals/page-header-title.tsx +++ b/packages/application-components/src/components/internals/page-header-title.tsx @@ -8,15 +8,11 @@ type TTitleSize = 'big' | 'medium' | 'small'; type Props = { title: string; - titleSize: TTitleSize; - truncate: boolean; + titleSize?: TTitleSize; + truncate?: boolean; subtitle?: string | ReactElement; children?: never; }; -const defaultProps: Pick = { - titleSize: 'small', - truncate: false, -}; const SubtitleWrapper = styled.div` margin-top: ${uiKitDesignTokens.spacing20}; @@ -50,10 +46,10 @@ const Title = (props: TitleProps) => { type SubtitleProps = { subtitle?: Props['subtitle']; - truncate: Props['truncate']; + truncate?: Props['truncate']; }; -const Subtitle = (props: SubtitleProps) => { +const Subtitle = ({ truncate = false, ...props }: SubtitleProps) => { if (!props.subtitle) { return null; } @@ -64,7 +60,7 @@ const Subtitle = (props: SubtitleProps) => { {props.subtitle} @@ -72,25 +68,21 @@ const Subtitle = (props: SubtitleProps) => { ); }; -Subtitle.defaultProps = { - truncate: false, -}; -const PageHeaderTitle = (props: Props) => ( +const PageHeaderTitle = ({ + titleSize = 'small', + truncate = false, + ...props +}: Props) => (
- - <Subtitle subtitle={props.subtitle} truncate={props.truncate} /> + <Title title={props.title} titleSize={titleSize} truncate={truncate} /> + <Subtitle subtitle={props.subtitle} truncate={truncate} /> </div> ); PageHeaderTitle.displayName = 'PageHeaderTitle'; -PageHeaderTitle.defaultProps = defaultProps; export default PageHeaderTitle; diff --git a/packages/application-components/src/components/internals/page-top-bar.tsx b/packages/application-components/src/components/internals/page-top-bar.tsx index 7ece544c4a..fc2ec38300 100644 --- a/packages/application-components/src/components/internals/page-top-bar.tsx +++ b/packages/application-components/src/components/internals/page-top-bar.tsx @@ -17,20 +17,19 @@ type MessageDescriptor = { }; type Label = string | MessageDescriptor; type Props = { - color: 'surface' | 'neutral'; - previousPathLabel: Label; + color?: 'surface' | 'neutral'; + previousPathLabel?: Label; onClick: ( event: MouseEvent<HTMLButtonElement> | KeyboardEvent<HTMLButtonElement> ) => void; children?: never; }; -const defaultProps: Pick<Props, 'color' | 'previousPathLabel'> = { - color: 'surface', - previousPathLabel: messages.back, -}; - -const PageTopBar = (props: Props) => { +const PageTopBar = ({ + color = 'surface', + previousPathLabel = messages.back, + ...props +}: Props) => { const intl = useIntl(); return ( @@ -47,9 +46,9 @@ const PageTopBar = (props: Props) => { <FlatButton tone="primary" label={ - typeof props.previousPathLabel === 'string' - ? props.previousPathLabel - : intl.formatMessage(props.previousPathLabel) + typeof previousPathLabel === 'string' + ? previousPathLabel + : intl.formatMessage(previousPathLabel) } icon={<ListIcon size="medium" color="primary" />} onClick={props.onClick} @@ -58,6 +57,5 @@ const PageTopBar = (props: Props) => { ); }; PageTopBar.displayName = 'PageTopBar'; -PageTopBar.defaultProps = defaultProps; export default PageTopBar; diff --git a/packages/application-components/src/components/main-pages/custom-form-main-page/custom-form-main-page.tsx b/packages/application-components/src/components/main-pages/custom-form-main-page/custom-form-main-page.tsx index 2b7bd1dccb..90a5306d19 100644 --- a/packages/application-components/src/components/main-pages/custom-form-main-page/custom-form-main-page.tsx +++ b/packages/application-components/src/components/main-pages/custom-form-main-page/custom-form-main-page.tsx @@ -58,7 +58,11 @@ type CustomFormMainPageProps = { hideDivider?: boolean; }; -const CustomFormMainPage = (props: CustomFormMainPageProps) => { +const CustomFormMainPage = ({ + hideControls = false, + hideDivider = false, + ...props +}: CustomFormMainPageProps) => { warning( props.title !== undefined || props.customTitleRow !== undefined, 'CustomFormMainPage: one of either `title` or `customTitleRow` is required but both their values are `undefined`' @@ -78,7 +82,7 @@ const CustomFormMainPage = (props: CustomFormMainPageProps) => { margin={headerRowMargin} customViewLocatorCode={props.customViewLocatorCode} /> - {!props.hideControls && props.formControls && ( + {!hideControls && props.formControls && ( <div css={css` margin: ${headerRowMargin}; @@ -89,7 +93,7 @@ const CustomFormMainPage = (props: CustomFormMainPageProps) => { </Spacings.Inline> </div> )} - {!props.hideDivider && ( + {!hideDivider && ( <div css={css` & > ${Divider} { @@ -106,16 +110,7 @@ const CustomFormMainPage = (props: CustomFormMainPageProps) => { ); }; -const defaultProps: Pick< - CustomFormMainPageProps, - 'hideControls' | 'hideDivider' -> = { - hideControls: false, - hideDivider: false, -}; - CustomFormMainPage.displayName = 'CustomFormMainPage'; -CustomFormMainPage.defaultProps = defaultProps; // Static export of pre-configured page header title component to easily // use as part of a custom title row diff --git a/packages/application-components/src/components/main-pages/form-main-page/form-main-page.tsx b/packages/application-components/src/components/main-pages/form-main-page/form-main-page.tsx index 5968d3ea9c..27923cad69 100644 --- a/packages/application-components/src/components/main-pages/form-main-page/form-main-page.tsx +++ b/packages/application-components/src/components/main-pages/form-main-page/form-main-page.tsx @@ -76,18 +76,17 @@ type FormMainPageProps = { onSecondaryButtonClick: (event: SyntheticEvent) => void; }; -const defaultProps: Pick<FormMainPageProps, 'hideControls'> = { - hideControls: false, -}; - -const FormMainPage = (props: FormMainPageProps) => { +const FormMainPage = ({ + hideControls = false, + ...props +}: FormMainPageProps) => { return ( <CustomFormMainPage title={props.title} subtitle={props.subtitle} customTitleRow={props.customTitleRow} customViewLocatorCode={props.customViewLocatorCode} - hideControls={props.hideControls} + hideControls={hideControls} formControls={ <> <CustomFormMainPage.FormSecondaryButton @@ -112,7 +111,6 @@ const FormMainPage = (props: FormMainPageProps) => { }; FormMainPage.displayName = 'FormMainPage'; -FormMainPage.defaultProps = defaultProps; // Static export of pre-configured page header title component to easily // use as part of a custom title row diff --git a/packages/application-components/src/components/main-pages/tabular-main-page/tabular-main-page.tsx b/packages/application-components/src/components/main-pages/tabular-main-page/tabular-main-page.tsx index fe586bf128..3793307cbf 100644 --- a/packages/application-components/src/components/main-pages/tabular-main-page/tabular-main-page.tsx +++ b/packages/application-components/src/components/main-pages/tabular-main-page/tabular-main-page.tsx @@ -49,18 +49,17 @@ type TTabularMainPageProps = { /** * Determines if the form controls should be rendered. */ - hideControls: boolean; + hideControls?: boolean; /** * These codes are used to configure which Custom Views are available for every tab. */ customViewLocatorCodes?: Record<string, LocationDescriptor>; }; -const defaultProps: Pick<TTabularMainPageProps, 'hideControls'> = { - hideControls: false, -}; - -const TabularMainPage = (props: TTabularMainPageProps) => { +const TabularMainPage = ({ + hideControls = false, + ...props +}: TTabularMainPageProps) => { const { currentCustomViewLocatorCode } = useCustomViewLocatorSelector( props.customViewLocatorCodes ); @@ -84,7 +83,7 @@ const TabularMainPage = (props: TTabularMainPageProps) => { tabControls={props.tabControls} formControls={ <FormControlsContainer> - {!props.hideControls && props.formControls && ( + {!hideControls && props.formControls && ( <Spacings.Inline alignItems="flex-end"> {props.formControls} </Spacings.Inline> @@ -110,7 +109,6 @@ const TabularMainPage = (props: TTabularMainPageProps) => { ); }; TabularMainPage.displayName = 'TabularMainPage'; -TabularMainPage.defaultProps = defaultProps; // Static export of pre-configured form control buttons to easily re-use // them in the custom controls. TabularMainPage.FormPrimaryButton = FormPrimaryButton; diff --git a/packages/application-components/src/components/modal-pages/form-modal-page/form-modal-page.tsx b/packages/application-components/src/components/modal-pages/form-modal-page/form-modal-page.tsx index d284f240e2..1e2dc6ad14 100644 --- a/packages/application-components/src/components/modal-pages/form-modal-page/form-modal-page.tsx +++ b/packages/application-components/src/components/modal-pages/form-modal-page/form-modal-page.tsx @@ -49,15 +49,11 @@ type Props = { labelSecondaryButton?: Label; onPrimaryButtonClick: (event: SyntheticEvent) => void; onSecondaryButtonClick: (event: SyntheticEvent) => void; - hideControls: boolean; + hideControls?: boolean; iconLeftSecondaryButton?: ReactElement; }; -const defaultProps: Pick<Props, 'hideControls'> = { - hideControls: false, -}; - -const FormModalPage = (props: Props) => ( +const FormModalPage = ({ hideControls = false, ...props }: Props) => ( <CustomFormModalPage title={props.title} subtitle={props.subtitle} @@ -68,7 +64,7 @@ const FormModalPage = (props: Props) => ( topBarPreviousPathLabel={props.topBarPreviousPathLabel} getParentSelector={props.getParentSelector} shouldDelayOnClose={props.shouldDelayOnClose} - hideControls={props.hideControls} + hideControls={hideControls} afterOpenStyles={props.afterOpenStyles} formControls={ <> @@ -93,7 +89,6 @@ const FormModalPage = (props: Props) => ( </CustomFormModalPage> ); FormModalPage.displayName = 'FormModalPage'; -FormModalPage.defaultProps = defaultProps; // This is a convenience proxy export to expose pre-defined Intl messages defined in the `@commercetools-frontend/i18n` package. // The Intl messages can be used for button labels. FormModalPage.Intl = sharedMessages; diff --git a/packages/application-components/src/components/modal-pages/internals/modal-page.tsx b/packages/application-components/src/components/modal-pages/internals/modal-page.tsx index 2779b54bbf..09652925ee 100644 --- a/packages/application-components/src/components/modal-pages/internals/modal-page.tsx +++ b/packages/application-components/src/components/modal-pages/internals/modal-page.tsx @@ -77,27 +77,24 @@ type Props = { * @deprecated Not used anymore, as the value is controlled via the Stacking Layer System. */ baseZIndex?: number; - getParentSelector: typeof getDefaultParentSelector; - shouldDelayOnClose: boolean; + getParentSelector?: typeof getDefaultParentSelector; + shouldDelayOnClose?: boolean; afterOpenStyles?: string | CSSObject; // TopBar props: topBarColor?: 'surface' | 'neutral'; currentPathLabel?: string; previousPathLabel?: Label; hidePathLabel?: boolean; - size: TModalPageSize; + size?: TModalPageSize; hideTopBar?: boolean; }; -const defaultProps: Pick< - Props, - 'getParentSelector' | 'shouldDelayOnClose' | 'size' -> = { - size: 'scale', - getParentSelector: getDefaultParentSelector, - shouldDelayOnClose: true, -}; -const ModalPage = (props: Props) => { +const ModalPage = ({ + size = 'scale', + getParentSelector = getDefaultParentSelector, + shouldDelayOnClose = true, + ...props +}: Props) => { const [forceClose, setForceClose] = useState(false); const closingTimer = useRef<NodeJS.Timeout>(); useEffect(() => { @@ -110,11 +107,11 @@ const ModalPage = (props: Props) => { }, [props.isOpen]); const { onClose } = props; - const TRANSITION_DURATION = stylesBySize[props.size].transitionTime; + const TRANSITION_DURATION = stylesBySize[size].transitionTime; const handleClose = useCallback( (event) => { - if (props.shouldDelayOnClose) { + if (shouldDelayOnClose) { // In this case we want the closing animation to be shown // and therefore we need wait for it to be completed // before calling `onClose`. @@ -126,7 +123,7 @@ const ModalPage = (props: Props) => { } onClose && onClose(event); }, - [onClose, props.shouldDelayOnClose, TRANSITION_DURATION] + [onClose, shouldDelayOnClose, TRANSITION_DURATION] ); return ( <ClassNames> @@ -138,22 +135,24 @@ const ModalPage = (props: Props) => { shouldCloseOnEsc={Boolean(props.onClose)} overlayElement={getOverlayElement} overlayClassName={{ - base: makeClassName(getOverlayStyles(props)), + base: makeClassName(getOverlayStyles({ size, ...props })), afterOpen: makeClassName(getAfterOpenOverlayAnimation()), beforeClose: makeClassName(getBeforeCloseOverlayAnimation()), }} className={{ - base: makeClassName(getContainerStyles(props)), + base: makeClassName(getContainerStyles({ size, ...props })), afterOpen: typeof props.afterOpenStyles === 'string' ? props.afterOpenStyles : makeClassName( props.afterOpenStyles ?? getAfterOpenContainerAnimation() ), - beforeClose: makeClassName(getBeforeCloseContainerAnimation(props)), + beforeClose: makeClassName( + getBeforeCloseContainerAnimation({ size, ...props }) + ), }} contentLabel={props.title} - parentSelector={props.getParentSelector} + parentSelector={getParentSelector} ariaHideApp={false} // Adjust this value if the (beforeClose) animation duration is changed closeTimeoutMS={TRANSITION_DURATION} @@ -180,6 +179,5 @@ const ModalPage = (props: Props) => { ); }; ModalPage.displayName = 'ModalPage'; -ModalPage.defaultProps = defaultProps; export default ModalPage; diff --git a/packages/application-components/src/components/modal-pages/tabular-modal-page/tabular-modal-page.tsx b/packages/application-components/src/components/modal-pages/tabular-modal-page/tabular-modal-page.tsx index 08b442ab50..8864135612 100644 --- a/packages/application-components/src/components/modal-pages/tabular-modal-page/tabular-modal-page.tsx +++ b/packages/application-components/src/components/modal-pages/tabular-modal-page/tabular-modal-page.tsx @@ -63,14 +63,10 @@ type Props = { // Header Props subtitle?: string | ReactElement; formControls?: ReactNode; - hideControls: boolean; + hideControls?: boolean; }; -const defaultProps: Pick<Props, 'hideControls'> = { - hideControls: false, -}; - -const TabularModalPage = (props: Props) => { +const TabularModalPage = ({ hideControls = false, ...props }: Props) => { const { currentCustomViewLocatorCode } = useCustomViewLocatorSelector( props.customViewLocatorCodes ); @@ -102,7 +98,7 @@ const TabularModalPage = (props: Props) => { tabControls={props.tabControls} formControls={ <FormControlsContainer> - {!props.hideControls && props.formControls && ( + {!hideControls && props.formControls && ( <Spacings.Inline alignItems="flex-end"> {props.formControls} </Spacings.Inline> @@ -123,7 +119,6 @@ const TabularModalPage = (props: Props) => { ); }; TabularModalPage.displayName = 'TabularModalPage'; -TabularModalPage.defaultProps = defaultProps; // Static export of pre-configured form control buttons to easily re-use // them in the custom controls. TabularModalPage.FormPrimaryButton = FormPrimaryButton; diff --git a/packages/application-components/src/components/modal-pages/utils/modal-page-top-bar.tsx b/packages/application-components/src/components/modal-pages/utils/modal-page-top-bar.tsx index 1c55b73c24..89f2c6b171 100644 --- a/packages/application-components/src/components/modal-pages/utils/modal-page-top-bar.tsx +++ b/packages/application-components/src/components/modal-pages/utils/modal-page-top-bar.tsx @@ -51,23 +51,20 @@ type MessageDescriptor = { }; type Label = string | MessageDescriptor; type Props = { - color: 'surface' | 'neutral'; + color?: 'surface' | 'neutral'; currentPathLabel?: string; - previousPathLabel: Label; + previousPathLabel?: Label; hidePathLabel?: boolean; onClose: (event: SyntheticEvent) => void; children?: never; }; -const defaultProps: Pick< - Props, - 'color' | 'previousPathLabel' | 'hidePathLabel' -> = { - color: 'surface', - previousPathLabel: messages.back, - hidePathLabel: false, -}; -const ModalPageTopBar = (props: Props) => { +const ModalPageTopBar = ({ + color = 'surface', + previousPathLabel = messages.back, + hidePathLabel = false, + ...props +}: Props) => { const intl = useIntl(); return ( <div @@ -101,13 +98,13 @@ const ModalPageTopBar = (props: Props) => { } `} > - {!props.hidePathLabel && ( + {!hidePathLabel && ( <FlatButton tone="primary" label={ - typeof props.previousPathLabel === 'string' - ? props.previousPathLabel - : intl.formatMessage(props.previousPathLabel) + typeof previousPathLabel === 'string' + ? previousPathLabel + : intl.formatMessage(previousPathLabel) } icon={<AngleLeftIcon size="medium" color="primary" />} onClick={props.onClose} @@ -138,6 +135,5 @@ const ModalPageTopBar = (props: Props) => { ); }; ModalPageTopBar.displayName = 'ModalPageTopBar'; -ModalPageTopBar.defaultProps = defaultProps; export default ModalPageTopBar; diff --git a/packages/application-components/src/components/page-content-containers/page-content-wide/page-content-wide.tsx b/packages/application-components/src/components/page-content-containers/page-content-wide/page-content-wide.tsx index 5aeb3940f9..080668d398 100644 --- a/packages/application-components/src/components/page-content-containers/page-content-wide/page-content-wide.tsx +++ b/packages/application-components/src/components/page-content-containers/page-content-wide/page-content-wide.tsx @@ -4,8 +4,8 @@ import { designTokens } from '@commercetools-uikit/design-system'; import { useWarning } from '@commercetools-uikit/utils'; export type TPageContentWide = { - columns: '1' | '1/1' | '2/1'; - gapSize: '10' | '20'; + columns?: '1' | '1/1' | '2/1'; + gapSize?: '10' | '20'; children: ReactNode; // @deprecated themeParentSelector?: () => HTMLElement | null; @@ -63,15 +63,18 @@ const Container = styled.div` width: 100%; `; -function PageContentWide(props: TPageContentWide) { +function PageContentWide({ + columns = '1', + gapSize = '20', + ...props +}: TPageContentWide) { const [leftChild, rightChild] = Children.toArray(props.children); const childrenCount = Children.count(props.children); - const isOneColumnAndMoreThanOneChild = - props.columns === '1' && childrenCount > 1; + const isOneColumnAndMoreThanOneChild = columns === '1' && childrenCount > 1; const isTwoColumnsAndMoreThanTwoChildren = - props.columns !== '1' && childrenCount > 2; + columns !== '1' && childrenCount > 2; useWarning( !isOneColumnAndMoreThanOneChild, @@ -85,14 +88,14 @@ function PageContentWide(props: TPageContentWide) { return ( <Container> - <Content columns={props.columns} gapSize={props.gapSize}> - {props.columns === '1' ? ( + <Content columns={columns} gapSize={gapSize}> + {columns === '1' ? ( <>{leftChild}</> ) : ( <> <LeftContentColumn>{leftChild}</LeftContentColumn> <RightContentColumn> - <RightColumnContentWrapper columns={props.columns}> + <RightColumnContentWrapper columns={columns}> {rightChild} </RightColumnContentWrapper> </RightContentColumn> @@ -103,10 +106,4 @@ function PageContentWide(props: TPageContentWide) { ); } -const defaultProps: Pick<TPageContentWide, 'columns' | 'gapSize'> = { - columns: '1', - gapSize: '20', -}; -PageContentWide.defaultProps = defaultProps; - export default PageContentWide; diff --git a/packages/application-components/src/components/public-page-layout/public-page-layout.tsx b/packages/application-components/src/components/public-page-layout/public-page-layout.tsx index fe7a4ac530..46a292eb4a 100644 --- a/packages/application-components/src/components/public-page-layout/public-page-layout.tsx +++ b/packages/application-components/src/components/public-page-layout/public-page-layout.tsx @@ -36,14 +36,20 @@ type TProps = { children: ReactNode; }; -const PublicPageLayoutContent: FC<TProps> = (props) => { - if (props.contentScale === 'wide') { +const PublicPageLayoutContent: FC<TProps> = ({ + contentScale = 'normal', + ...props +}) => { + if (contentScale === 'wide') { return <ContainerColumnWide>{props.children}</ContainerColumnWide>; } return <ContainerColumn>{props.children}</ContainerColumn>; }; -const PublicPageLayout: FC<TProps> = (props) => { +const PublicPageLayout: FC<TProps> = ({ + contentScale = 'normal', + ...props +}) => { return ( <Container> <Spacings.Stack scale="xl" alignItems="center"> @@ -68,10 +74,10 @@ const PublicPageLayout: FC<TProps> = (props) => { )} <Spacings.Stack scale="xl"> <PublicPageLayoutContent {...props} /> - <PublicPageLayoutContent contentScale={props.contentScale}> + <PublicPageLayoutContent contentScale={contentScale}> <Spacings.Stack scale="xs" - alignItems={props.contentScale === 'wide' ? 'center' : 'stretch'} + alignItems={contentScale === 'wide' ? 'center' : 'stretch'} > {props.legalMessage && ( <Text.Detail tone="secondary">{props.legalMessage}</Text.Detail> @@ -87,8 +93,5 @@ const PublicPageLayout: FC<TProps> = (props) => { ); }; PublicPageLayout.displayName = 'PublicPageLayout'; -PublicPageLayout.defaultProps = { - contentScale: 'normal', -}; export default PublicPageLayout; diff --git a/packages/application-components/src/components/tab-header/tab-header.tsx b/packages/application-components/src/components/tab-header/tab-header.tsx index ecb12117e1..835806b9da 100644 --- a/packages/application-components/src/components/tab-header/tab-header.tsx +++ b/packages/application-components/src/components/tab-header/tab-header.tsx @@ -43,11 +43,11 @@ export type TTabHeaderProps = { /** * If `true`, indicates that the element is in a disabled state. */ - isDisabled: boolean; + isDisabled?: boolean; /** * If `true`, marks the tab as active if the link matches exactly the route. */ - exactPathMatch: boolean; + exactPathMatch?: boolean; }; const TabLabel = ({ children }: { children?: string }) => { @@ -58,25 +58,28 @@ const TabLabel = ({ children }: { children?: string }) => { ); }; -export const TabHeader = (props: TTabHeaderProps) => { +export const TabHeader = ({ + isDisabled = false, + exactPathMatch = false, + ...props +}: TTabHeaderProps) => { const intl = useIntl(); const location = useLocation(); const isActive = Boolean( matchPath(location.pathname, { // strip the search, otherwise the path won't match path: pathWithoutSearch(props.to), - exact: props.exactPathMatch, + exact: exactPathMatch, strict: false, }) ); - const isDisabled = props.isDisabled; let label = props.label; if (props.intlMessage) { label = intl.formatMessage(props.intlMessage); } - warnIfMissingContent(props); + warnIfMissingContent({ exactPathMatch, isDisabled, ...props }); return ( <Link @@ -93,10 +96,4 @@ export const TabHeader = (props: TTabHeaderProps) => { TabHeader.displayName = 'TabHeader'; -const defaultProps: Pick<TTabHeaderProps, 'isDisabled' | 'exactPathMatch'> = { - isDisabled: false, - exactPathMatch: false, -}; -TabHeader.defaultProps = defaultProps; - export default TabHeader; diff --git a/packages/application-shell/src/components/application-page-title/application-page-title.tsx b/packages/application-shell/src/components/application-page-title/application-page-title.tsx index 8d7e42ab1d..fc1f33b4bd 100644 --- a/packages/application-shell/src/components/application-page-title/application-page-title.tsx +++ b/packages/application-shell/src/components/application-page-title/application-page-title.tsx @@ -18,16 +18,12 @@ type TApplicationPageTitleProps = { * <ApplicationPageTitle additionalParts={['T-Shirt red']} /> * // T-Shirt red - Products - my-shop - Merchant Center */ - additionalParts: string[]; + additionalParts?: string[]; }; const maxTitleCharLength = 24; const staticPaths = ['account', 'login']; -const defaultProps: TApplicationPageTitleProps = { - additionalParts: [], -}; - const getPageTitle = (pathname: string, additionalParts: string[]) => { const [, projectKeyOrStaticPath, entryPointUriPath] = pathname.split('/'); @@ -57,17 +53,18 @@ const getPageTitle = (pathname: string, additionalParts: string[]) => { ].join(' - '); }; -const ApplicationPageTitle = (props: TApplicationPageTitleProps) => { +const ApplicationPageTitle = ({ + additionalParts = [], +}: TApplicationPageTitleProps) => { const location = useLocation(); useLayoutEffect(() => { - const pageTitle = getPageTitle(location.pathname, props.additionalParts); + const pageTitle = getPageTitle(location.pathname, additionalParts); document.title = pageTitle; - }, [location.pathname, props.additionalParts]); + }, [location.pathname, additionalParts]); return null; }; ApplicationPageTitle.displayName = 'ApplicationPageTitle'; -ApplicationPageTitle.defaultProps = defaultProps; export default ApplicationPageTitle; diff --git a/packages/application-shell/src/components/inject-reducers/inject-reducers.tsx b/packages/application-shell/src/components/inject-reducers/inject-reducers.tsx index a78424ce80..acd8cf3668 100644 --- a/packages/application-shell/src/components/inject-reducers/inject-reducers.tsx +++ b/packages/application-shell/src/components/inject-reducers/inject-reducers.tsx @@ -6,15 +6,11 @@ import type { TEnhancedStore } from '../../configure-store'; type Props = { id: string; reducers: ReducersMapObject; - shouldCleanUpOnUnmount: boolean; + shouldCleanUpOnUnmount?: boolean; children: ReactNode; }; -const defaultProps: Pick<Props, 'shouldCleanUpOnUnmount'> = { - shouldCleanUpOnUnmount: true, -}; - -const InjectReducers = (props: Props) => { +const InjectReducers = ({ shouldCleanUpOnUnmount = true, ...props }: Props) => { const [areReducersInjected, setAreReducersInjected] = useState(false); const store = useStore() as TEnhancedStore; @@ -26,17 +22,16 @@ const InjectReducers = (props: Props) => { setAreReducersInjected(true); return () => { - if (props.shouldCleanUpOnUnmount) { + if (shouldCleanUpOnUnmount) { store.removeReducers({ id: props.id }); } }; - }, [props.id, props.reducers, props.shouldCleanUpOnUnmount, store]); + }, [props.id, props.reducers, shouldCleanUpOnUnmount, store]); // Render children only when the plugin reducers have been injected if (areReducersInjected) return <>{props.children}</>; return null; }; InjectReducers.displayName = 'InjectReducers'; -InjectReducers.defaultProps = defaultProps; export default InjectReducers; diff --git a/packages/application-shell/src/components/navbar/menu-items.tsx b/packages/application-shell/src/components/navbar/menu-items.tsx index 63ab4ea895..e111713120 100644 --- a/packages/application-shell/src/components/navbar/menu-items.tsx +++ b/packages/application-shell/src/components/navbar/menu-items.tsx @@ -282,16 +282,14 @@ MenuItem.displayName = 'MenuItem'; export type MenuItemLinkProps = { linkTo?: string; - exactMatch: boolean; + exactMatch?: boolean; children: ReactNode; onClick?: (event: SyntheticEvent<HTMLAnchorElement>) => void; useFullRedirectsForLinks?: boolean; isSubmenuLink?: boolean; isSubmenuFocused?: boolean; }; -const menuItemLinkDefaultProps: Pick<MenuItemLinkProps, 'exactMatch'> = { - exactMatch: false, -}; + const NavLinkWrapper = (props: MenuItemLinkProps) => { const Wrapper = props.isSubmenuLink ? TextLinkSublistWrapper : Fragment; return <Wrapper>{props.children}</Wrapper>; @@ -301,15 +299,15 @@ const NavLinkClickableContentWrapper = (props: MenuItemLinkProps) => { return <Wrapper>{props.children}</Wrapper>; }; -const MenuItemLink = (props: MenuItemLinkProps) => { +const MenuItemLink = ({ exactMatch = false, ...props }: MenuItemLinkProps) => { const redirectTo = (targetUrl: string) => location.replace(targetUrl); if (props.linkTo) { const linkLevel = props.isSubmenuLink ? 'text-link-sublist' : 'text-link'; return ( - <NavLinkWrapper {...props}> + <NavLinkWrapper exactMatch={exactMatch} {...props}> <NavLink to={props.linkTo} - exact={props.exactMatch} + exact={exactMatch} activeClassName="highlighted" data-link-level={linkLevel} css={getMenuItemLinkStyles(Boolean(props.isSubmenuLink))} @@ -324,7 +322,7 @@ const MenuItemLink = (props: MenuItemLinkProps) => { } }} > - <NavLinkClickableContentWrapper {...props}> + <NavLinkClickableContentWrapper exactMatch={exactMatch} {...props}> {props.children} </NavLinkClickableContentWrapper> </NavLink> @@ -334,7 +332,6 @@ const MenuItemLink = (props: MenuItemLinkProps) => { return <>{props.children}</>; }; MenuItemLink.displayName = 'MenuItemLink'; -MenuItemLink.defaultProps = menuItemLinkDefaultProps; const isEveryMenuVisibilitySetToHidden = ( menuVisibilities?: TNormalizedMenuVisibilities | null, @@ -362,18 +359,16 @@ type TLongLivedFlag = { value: boolean; reason?: string; }; -const restrictedMenuItemDefaultProps: Pick< - RestrictedMenuItemProps, - 'permissions' -> = { - permissions: [], -}; + function isLongLivedFlag( flag: TFlagVariation | TLongLivedFlag ): flag is TLongLivedFlag { return typeof (flag as TLongLivedFlag)?.value === 'boolean'; } -const RestrictedMenuItem = (props: RestrictedMenuItemProps) => { +const RestrictedMenuItem = ({ + permissions = [], + ...props +}: RestrictedMenuItemProps) => { // NOTE: Custom application are activated/deactivated while their // visibility is not controlled via a visibiility overwrite. const flagVariation = useFlagVariation(props.featureToggle); @@ -386,10 +381,10 @@ const RestrictedMenuItem = (props: RestrictedMenuItemProps) => { return null; const permissionsWrapper = - (Array.isArray(props.permissions) && props.permissions.length > 0) || + (Array.isArray(permissions) && permissions.length > 0) || (Array.isArray(props.dataFences) && props.dataFences.length > 0) ? ( <RestrictedByPermissions - permissions={props.permissions} + permissions={permissions} actionRights={props.actionRights} dataFences={props.dataFences} selectDataFenceData={(demandedDataFence) => { @@ -423,7 +418,6 @@ const RestrictedMenuItem = (props: RestrictedMenuItemProps) => { return permissionsWrapper; }; RestrictedMenuItem.displayName = 'RestrictedMenuItem'; -RestrictedMenuItem.defaultProps = restrictedMenuItemDefaultProps; type MenuLabelProps = { labelAllLocales: TLocalizedField[]; diff --git a/packages/application-shell/src/components/navbar/navbar-skeleton.tsx b/packages/application-shell/src/components/navbar/navbar-skeleton.tsx index ad1c5c6e77..8e3dfc8dac 100644 --- a/packages/application-shell/src/components/navbar/navbar-skeleton.tsx +++ b/packages/application-shell/src/components/navbar/navbar-skeleton.tsx @@ -12,7 +12,8 @@ import { type TMenuItemProps, } from './navbar-skeleton.styles'; -const MenuItem = (props: TMenuItemProps & TNavBarSkeletonProps) => { +type TMenuItemComponentProps = TMenuItemProps & TNavBarSkeletonProps; +const MenuItem = (props: TMenuItemComponentProps) => { return ( <MenuItemContainer isExpanded={props.isExpanded} @@ -23,9 +24,6 @@ const MenuItem = (props: TMenuItemProps & TNavBarSkeletonProps) => { </MenuItemContainer> ); }; -MenuItem.defaultProps = { - contentWidth: 'wide', -}; const NavBarSkeleton = (props: TNavBarSkeletonProps) => { return ( @@ -39,16 +37,24 @@ const NavBarSkeleton = (props: TNavBarSkeletonProps) => { <NavBarBody> <MenuItemGroup> {[...Array(2).keys()].map((index) => ( - <MenuItem key={index} isExpanded={props.isExpanded} /> + <MenuItem + key={index} + contentWidth="wide" + isExpanded={props.isExpanded} + /> ))} </MenuItemGroup> <MenuItemGroup> {[...Array(10).keys()].map((index) => ( - <MenuItem key={index} isExpanded={props.isExpanded} /> + <MenuItem + key={index} + contentWidth="wide" + isExpanded={props.isExpanded} + /> ))} </MenuItemGroup> <MenuItemGroup> - <MenuItem isExpanded={props.isExpanded} /> + <MenuItem contentWidth="wide" isExpanded={props.isExpanded} /> </MenuItemGroup> </NavBarBody> <NavBarFooter isExpanded={props.isExpanded}> diff --git a/packages/permissions/src/components/authorized/authorized.tsx b/packages/permissions/src/components/authorized/authorized.tsx index 4532680ae3..f79852f39b 100644 --- a/packages/permissions/src/components/authorized/authorized.tsx +++ b/packages/permissions/src/components/authorized/authorized.tsx @@ -44,24 +44,23 @@ type Props = { render: (isAuthorized: boolean) => ReactNode; children?: never; }; -const defaultProps: Pick<Props, 'shouldMatchSomePermissions'> = { - shouldMatchSomePermissions: false, -}; -const Authorized = (props: Props) => { +const Authorized = ({ + shouldMatchSomePermissions = false, + ...props +}: Props) => { const isAuthorized = useIsAuthorized({ demandedPermissions: props.demandedPermissions, demandedActionRights: props.demandedActionRights, demandedDataFences: props.demandedDataFences, selectDataFenceData: props.selectDataFenceData, - shouldMatchSomePermissions: props.shouldMatchSomePermissions, + shouldMatchSomePermissions: shouldMatchSomePermissions, projectPermissions: props.projectPermissions, }); return <>{props.render(isAuthorized)}</>; }; Authorized.displayName = 'Authorized'; -Authorized.defaultProps = defaultProps; type TInjectAuthorizedOptions<OwnProps extends {}> = { shouldMatchSomePermissions?: boolean; diff --git a/packages/react-notifications/src/components/notification/notification.tsx b/packages/react-notifications/src/components/notification/notification.tsx index dfec3a2b51..672f370f84 100644 --- a/packages/react-notifications/src/components/notification/notification.tsx +++ b/packages/react-notifications/src/components/notification/notification.tsx @@ -62,15 +62,12 @@ NotificationIcon.displayName = 'NotificationIcon'; export type Props = { domain: TAppNotificationDomain; type: TAppNotificationKind; - fixed: boolean; + fixed?: boolean; onCloseClick?: (event: SyntheticEvent) => void; children: ReactNode; }; -const defaultProps: Pick<Props, 'fixed'> = { - fixed: false, -}; -const Notification = (props: Props) => { +const Notification = ({ fixed = false, ...props }: Props) => { const intl = useIntl(); const id = useFieldId(undefined, sequentialId); @@ -78,14 +75,14 @@ const Notification = (props: Props) => { <div role="alertdialog" aria-describedby={id} - css={getStylesForNotification(props)} + css={getStylesForNotification({ fixed, ...props })} {...filterDataAttributes(props)} > - <div id={id} css={getStylesForContent(props)}> + <div id={id} css={getStylesForContent({ fixed, ...props })}> {props.children} </div> {props.onCloseClick ? ( - <div css={getStylesForCloseIcon(props)}> + <div css={getStylesForCloseIcon({ fixed, ...props })}> <SecondaryIconButton label={intl.formatMessage(messages.hideNotification)} onClick={props.onCloseClick} @@ -95,7 +92,7 @@ const Notification = (props: Props) => { </div> ) : null} {props.domain === NOTIFICATION_DOMAINS.SIDE ? ( - <div css={getStylesForNotificationIcon(props)}> + <div css={getStylesForNotificationIcon({ fixed, ...props })}> <NotificationIcon type={props.type} color="surface" /> </div> ) : null} @@ -103,6 +100,5 @@ const Notification = (props: Props) => { ); }; Notification.displayName = 'Notification'; -Notification.defaultProps = defaultProps; export default Notification; diff --git a/packages/react-notifications/src/components/notifier/notifier.tsx b/packages/react-notifications/src/components/notifier/notifier.tsx index 3c8374c181..1cd0e5e0b3 100644 --- a/packages/react-notifications/src/components/notifier/notifier.tsx +++ b/packages/react-notifications/src/components/notifier/notifier.tsx @@ -17,19 +17,19 @@ type Props = { meta?: { [key: string]: unknown }; dismissAfter?: number; }; -const defaultProps: Pick<Props, 'domain' | 'kind'> = { - domain: NOTIFICATION_DOMAINS.SIDE, - kind: NOTIFICATION_KINDS_SIDE.success, -}; -const Notifier = (props: Props) => { +const Notifier = ({ + domain = NOTIFICATION_DOMAINS.SIDE, + kind = NOTIFICATION_KINDS_SIDE.success, + ...props +}: Props) => { const showNotification = useShowNotification<Props>(); useEffect(() => { const notification = showNotification( { - domain: props.domain, - kind: props.kind, + domain: domain, + kind: kind, text: props.text, }, isNumber(props.dismissAfter) @@ -46,6 +46,5 @@ const Notifier = (props: Props) => { return null; }; Notifier.displayName = 'Notifier'; -Notifier.defaultProps = defaultProps; export default Notifier; diff --git a/packages/sdk/src/components/sdk-get/sdk-get.tsx b/packages/sdk/src/components/sdk-get/sdk-get.tsx index ac2ebe6cfb..7f705922a2 100644 --- a/packages/sdk/src/components/sdk-get/sdk-get.tsx +++ b/packages/sdk/src/components/sdk-get/sdk-get.tsx @@ -45,10 +45,6 @@ export class SdkGet extends Component<Props, State> { static errorHandler: StaticErrorHandler = (error: TSdkError) => { throw error; }; - static defaultProps: Pick<Props, 'actionCreatorArgs' | 'shouldRefetch'> = { - actionCreatorArgs: [], - shouldRefetch: (prevArgs, nextArgs) => !deepEqual(prevArgs, nextArgs), - }; state = { // We want the component to have a loading state by default, so we // keep track of whether the first request has completed. @@ -58,6 +54,11 @@ export class SdkGet extends Component<Props, State> { requestsInFlight: 0, result: undefined, error: undefined, + actionCreatorArgs: [], + shouldRefetch: ( + prevArgs: TActionCreatorArgs, + nextArgs: TActionCreatorArgs + ) => !deepEqual(prevArgs, nextArgs), }; isComponentMounted = false; changeRequestsInFlight = (delta: number) => {