Skip to content

Commit 537e696

Browse files
authored
refactor(collapsible panel): migrate to TS (#2008)
* refactor(collapsible-panel): change filetypes to TS * refactor(collapsible-panel): add some types (temp commit) * refactor(collapsible-panel): fully migrate to TS, fix test * refactor(collapsible-panel): add changeset * refactor(collapsible-panel): generate readme * refactor(collapsible-panel): format files * refactor(collapsible-panel): implement refactor feedback * refactor(collapsible-panel): implement extract defaultProps to a variable
1 parent 721bab8 commit 537e696

14 files changed

+252
-198
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@commercetools-uikit/collapsible-motion': patch
3+
---
4+
5+
Update types for props

.changeset/witty-dolls-lie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@commercetools-uikit/collapsible-panel': patch
3+
---
4+
5+
Migrate to Typescript

packages/components/collapsible-motion/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ export default Example;
7878
### Signature `children`
7979

8080
```ts
81-
({ isOpen, containerStyles, toggle, registerContentNode }: TRenderFunction) =>
82-
ReactNode;
81+
(options: TRenderFunctionOptions) => ReactNode;
8382
```
8483

8584
### Signature `onToggle`

packages/components/collapsible-motion/src/collapsible-motion.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { useRef, useEffect, useCallback, ReactNode } from 'react';
1+
import {
2+
useRef,
3+
useEffect,
4+
useCallback,
5+
ReactNode,
6+
CSSProperties,
7+
LegacyRef,
8+
} from 'react';
29
import { warning } from '@commercetools-uikit/utils';
310
import { keyframes, ClassNames } from '@emotion/react';
411

@@ -11,13 +18,13 @@ type TContainerStyles = {
1118
visibility?: string;
1219
name?: string;
1320
animation?: string;
14-
};
21+
} & CSSProperties;
1522

1623
type TRenderFunctionOptions = {
1724
isOpen: boolean;
1825
containerStyles: TContainerStyles;
1926
toggle: () => void;
20-
registerContentNode: ReactNode;
27+
registerContentNode: TNodeRefObject;
2128
};
2229

2330
export type TCollapsibleMotionProps = {
@@ -74,7 +81,7 @@ const createClosingAnimation = (height: number, minHeight = 0) =>
7481

7582
type TNodeRefObject = {
7683
clientHeight: number;
77-
};
84+
} & LegacyRef<HTMLDivElement>;
7885

7986
const useToggleAnimation = (
8087
isOpen: boolean,
@@ -159,7 +166,7 @@ const ControlledCollapsibleMotion = (props: TCollapsibleMotionProps) => {
159166
...animationStyle,
160167
},
161168
toggle: animationToggle as () => void,
162-
registerContentNode,
169+
registerContentNode: registerContentNode as TNodeRefObject,
163170
});
164171
}}
165172
</ClassNames>
@@ -199,7 +206,7 @@ const UncontrolledCollapsibleMotion = (props: TCollapsibleMotionProps) => {
199206
...animationStyle,
200207
},
201208
toggle: animationToggle as () => void,
202-
registerContentNode,
209+
registerContentNode: registerContentNode as TNodeRefObject,
203210
});
204211
}}
205212
</ClassNames>

packages/components/collapsible-panel/README.md

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,34 @@ export default Example;
6161

6262
## Properties
6363

64-
| Props | Type | Required | Default | Description |
65-
| ------------------------- | ----------------------------------------------------------------------------------------- | :------: | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66-
| `id` | `string` | | `uniqueId()` | An unique id for the panel header, which will also be used to generate a prefixed id for the panel content section.&#xA;<br/>&#xA;Read about `getPanelContentId` below for more about this. |
67-
| `header` | `node` || | The title being rendered at top left of the panel |
68-
| `secondaryHeader` | `node` | | | A secondary header for the panel (only pass if needed) |
69-
| `description` | `string` | | | If passed will be shown below the title as more information regarding the panel |
70-
| `className` | `string` | | | |
71-
| `isSticky` | `bool` | | | Makes the panel's header sticky in regards to the page's scroll |
72-
| `headerControls` | `node` | | | Controls at the top right part of the panel |
73-
| `isDisabled` | `bool` | | `false` | Disables the panel and all interactions with it |
74-
| `children` | `node` | | | The actual content rendered inside the panel |
75-
| `tone` | `enum`<br/>Possible values:<br/>`'urgent', 'primary'` | | | |
76-
| `theme` | `enum`<br/>Possible values:<br/>`'dark', 'light'` | | `'dark'` | The main color combination of the for the panel header and container |
77-
| `condensed` | `bool` | | `false` | Whenever `true` the headers and content itself&#xA;will consume less space in that to the borders are smaller and everything has less padding |
78-
| `hideExpansionControls` | `bool` | | | Controls the visibility of the expansion controls on the left |
79-
| `headerControlsAlignment` | `enum`<br/>Possible values:<br/>`'left', 'right'` | | `'right'` | |
80-
| `isDefaultClosed` | `custom` | | | Indicates if the panel's content should be collapsed or shown by default.&#xA;<br />&#xA;Updates to this value are not respected. Only used for **uncontrolled** mode (when no`onToggle` is passed.) |
81-
| `isClosed` | `bool` | | | Indicates if the panel's content should be collapsed or shown.&#xA;<br />&#xA;Component becomes \*_controlled_ when this is passed. |
82-
| `onToggle` | `custom` | | | function to be triggered whenever the user clicks the top area to collapse the panel's content&#xA;<br />&#xA;Becomes required when `isClosed` is passed.&#xA;<br />&#xA;Signature: `() => void` |
83-
| `horizontalConstraint` | `enum`<br/>Possible values:<br/>`6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 'scale', 'auto'` | | `'scale'` | Horizontal size limit of the input fields. |
64+
| Props | Type | Required | Default | Description |
65+
| ------------------------- | -------------------------------------------------------------------------------------------- | :------: | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66+
| `id` | `string` | | `uniqueId()` | An unique id for the panel header, which will also be used to generate a prefixed id for the panel content section.&#xA;<br/>&#xA;Read about `getPanelContentId` below for more about this. |
67+
| `header` | `ReactNode` || | The title being rendered at top left of the panel |
68+
| `secondaryHeader` | `ReactNode` | | | A secondary header for the panel (only pass if needed) |
69+
| `description` | `string` | | | If passed will be shown below the title as more information regarding the panel |
70+
| `className` | `string` | | | |
71+
| `isSticky` | `boolean` | | | Makes the panel's header sticky in regards to the page's scroll |
72+
| `headerControls` | `ReactNode` | | | Controls at the top right part of the panel |
73+
| `isDisabled` | `boolean` | | `false` | Disables the panel and all interactions with it |
74+
| `children` | `ReactNode` | | | The actual content rendered inside the panel |
75+
| `tone` | `union`<br/>Possible values:<br/>`'urgent' , 'primary'` | | | |
76+
| `theme` | `union`<br/>Possible values:<br/>`'dark' , 'light'` | | `'dark'` | The main color combination of the for the panel header and container |
77+
| `condensed` | `boolean` | | `false` | Whenever `true` the headers and content itself&#xA;will consume less space in that to the borders are smaller and everything has less padding |
78+
| `hideExpansionControls` | `boolean` | | | Controls the visibility of the expansion controls on the left |
79+
| `headerControlsAlignment` | `union`<br/>Possible values:<br/>`'left' , 'right'` | | `'right'` | |
80+
| `isDefaultClosed` | `boolean` | | | Indicates if the panel's content should be collapsed or shown by default.&#xA;<br />&#xA;Updates to this value are not respected. Only used for **uncontrolled** mode (when no`onToggle` is passed.) |
81+
| `isClosed` | `boolean` | | | Indicates if the panel's content should be collapsed or shown.&#xA;<br />&#xA;Component becomes \*_controlled_ when this is passed. |
82+
| `onToggle` | `Function`<br/>[See signature.](#signature-onToggle) | | | function to be triggered whenever the user clicks the top area to collapse the panel's content&#xA;<br />&#xA;Becomes required when `isClosed` is passed.&#xA;<br />&#xA;Signature: `() => void` |
83+
| `horizontalConstraint` | `union`<br/>Possible values:<br/>`, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 'scale', 'auto'` | | `'scale'` | Horizontal size limit of the input fields. |
84+
85+
## Signatures
86+
87+
### Signature `onToggle`
88+
89+
```ts
90+
() => void
91+
```
8492

8593
## Where to use
8694

Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import PropTypes from 'prop-types';
1+
import { ReactNode } from 'react';
22
import Text from '@commercetools-uikit/text';
33

4-
const CollapsiblePanelHeader = (props) => (
4+
type TCollapsiblePanelHeader = {
5+
children: ReactNode;
6+
};
7+
const CollapsiblePanelHeader = (props: TCollapsiblePanelHeader) => (
58
<Text.Subheadline as="h4" isBold={true} truncate={true}>
69
{props.children}
710
</Text.Subheadline>
811
);
912

1013
CollapsiblePanelHeader.displayName = 'CollapsiblePanelHeader';
11-
CollapsiblePanelHeader.propTypes = {
12-
children: PropTypes.node.isRequired,
13-
};
1414

1515
export default CollapsiblePanelHeader;

packages/components/collapsible-panel/src/collapsible-panel.spec.js

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import PropTypes from 'prop-types';
2+
import { warning } from '@commercetools-uikit/utils';
23
import { screen, render } from '../../../../test/test-utils';
34
import CollapsiblePanel from './collapsible-panel';
45

@@ -10,6 +11,11 @@ HeaderControls.propTypes = {
1011
onClick: PropTypes.func,
1112
};
1213

14+
jest.mock('@commercetools-uikit/utils', () => ({
15+
...jest.requireActual('@commercetools-uikit/utils'),
16+
warning: jest.fn(),
17+
}));
18+
1319
it('should smoke', () => {
1420
render(<CollapsiblePanel header="Header">Children</CollapsiblePanel>);
1521
expect(screen.getByText('Header')).toBeInTheDocument();
@@ -86,14 +92,12 @@ describe('when isDefaultClosed and isClosed are passed', () => {
8692
Children
8793
</CollapsiblePanel>
8894
);
89-
expect(console.error).toHaveBeenCalledWith(
90-
expect.stringMatching(/Warning/),
91-
'prop',
92-
expect.stringMatching(/Invalid prop `isDefaultClosed` supplied to (.*)/),
93-
expect.any(String)
95+
96+
expect(warning).toHaveBeenCalledWith(
97+
expect.any(Boolean),
98+
expect.stringMatching(/Invalid prop `isDefaultClosed` supplied to (.*)/i)
9499
);
95100
});
96-
/* eslint-enable no-console */
97101
});
98102

99103
describe('when onToggle is provided without isClosed', () => {
@@ -112,14 +116,11 @@ describe('when onToggle is provided without isClosed', () => {
112116
Children
113117
</CollapsiblePanel>
114118
);
115-
expect(console.error).toHaveBeenCalledWith(
116-
expect.stringMatching(/Warning/),
117-
'prop',
118-
expect.stringMatching(/Invalid prop `onToggle` supplied to (.*)/),
119-
expect.any(String)
119+
expect(warning).toHaveBeenCalledWith(
120+
expect.any(Boolean),
121+
expect.stringMatching(/Invalid prop `onToggle` supplied to (.*)/i)
120122
);
121123
});
122-
/* eslint-enable no-console */
123124
});
124125

125126
it('should call "onToggle" when header is clicked', () => {
@@ -193,7 +194,10 @@ describe('aria attributes', () => {
193194
};
194195
};
195196
it('should have a valid aria-controls correspondence', () => {
196-
const { getPanelHeader, getPanelContent } = renderPanel({ id: 'test-id' });
197+
const { getPanelHeader, getPanelContent } = renderPanel({
198+
id: 'test-id',
199+
isClosed: false,
200+
});
197201

198202
const panelContentId = CollapsiblePanel.getPanelContentId('test-id');
199203

packages/components/collapsible-panel/src/collapsible-panel.styles.js renamed to packages/components/collapsible-panel/src/collapsible-panel.styles.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { css } from '@emotion/react';
22
import styled from '@emotion/styled';
33
import { customProperties as vars } from '@commercetools-uikit/design-system';
4+
import type { TCollapsiblePanel } from './collapsible-panel';
45

5-
function getThemeStyle(theme) {
6+
function getThemeStyle(theme?: TCollapsiblePanel['theme']) {
67
if (theme === 'light') {
78
return css`
89
background-color: ${vars.colorSurface};
@@ -13,7 +14,13 @@ function getThemeStyle(theme) {
1314
`;
1415
}
1516

16-
const getHeaderContainerStyles = (props, isOpen) => {
17+
const getHeaderContainerStyles = (
18+
props: Pick<
19+
TCollapsiblePanel,
20+
'headerControlsAlignment' | 'condensed' | 'isDisabled' | 'isSticky'
21+
>,
22+
isOpen: boolean
23+
) => {
1724
const baseStyles = css`
1825
position: relative;
1926
border-top-left-radius: ${vars.borderRadius6};

0 commit comments

Comments
 (0)