-
Notifications
You must be signed in to change notification settings - Fork 217
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(action-bar)!: Refactor ActionBar component (#1396)
- Convert to compound component. - Remove `ChildrenContainer` and replace it styles to `ActionBarContainer`. - Update documentation - Add jest component test and visual tests for storybook. Resolves: #1382 [category:Components] ### BREAKING CHANGES Change `fixed` prop from component to `position` to set container position (`fixed` position has been set as default).
- Loading branch information
1 parent
0e355f8
commit b7fc2fe
Showing
10 changed files
with
280 additions
and
173 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import {API, FileInfo, Options, JSXElement, ImportDeclaration, ASTPath} from 'jscodeshift'; | ||
import {getImportRenameMap} from './utils/getImportRenameMap'; | ||
|
||
const mainPackage = '@workday/canvas-kit-react'; | ||
const actionBarPackage = '@workday/canvas-kit-react/action-bar'; | ||
|
||
export default function transformer(file: FileInfo, api: API, options: Options) { | ||
const j = api.jscodeshift; | ||
|
||
const root = j(file.source); | ||
|
||
let hasActionBarImports = false; | ||
|
||
// This toggles the failsafe that prevents us from accidentally transforming something unintentionally. | ||
root.find(j.ImportDeclaration, (nodePath: ImportDeclaration) => { | ||
const {value} = nodePath.source; | ||
// If there's an import from the action-bar package, set the import boolean check to true | ||
if (value === actionBarPackage) { | ||
hasActionBarImports = true; | ||
return; | ||
} | ||
// If there's an import from the main package, check to see if ActionBar is among the named imports | ||
// e.g. import ActionBar from '@workday/canvas-kit-react'; | ||
if (value === mainPackage) { | ||
(nodePath.specifiers || []).forEach(specifier => { | ||
if (specifier.type === 'ImportSpecifier') { | ||
const specifierName = specifier.imported.name; | ||
if (specifierName === 'ActionBar') { | ||
hasActionBarImports = true; | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
|
||
// Failsafe to skip transforms unless a ActionBar import is detected | ||
if (!hasActionBarImports) { | ||
return root.toSource(); | ||
} | ||
|
||
// Replace default import with named or renamed import | ||
root.find(j.ImportDefaultSpecifier).forEach(nodePath => { | ||
const parent = nodePath.parent as ASTPath<ImportDeclaration>; | ||
const importSource = String(parent.node.source.value) as typeof actionBarPackage; | ||
const localName = nodePath.value.local?.name; | ||
if (actionBarPackage === importSource && localName) { | ||
nodePath.replace(j.importSpecifier(j.identifier('ActionBar'), j.identifier(localName))); | ||
} | ||
}); | ||
|
||
const {containsCanvasImports, importMap, styledMap} = getImportRenameMap( | ||
j, | ||
root, | ||
'@workday/canvas-kit-react/action-bar' | ||
); | ||
|
||
if (!containsCanvasImports) { | ||
return file.source; | ||
} | ||
|
||
// Remove fixed prop from ActionBar component | ||
root | ||
.find( | ||
j.JSXElement, | ||
(value: JSXElement) => | ||
value.openingElement.name.type === 'JSXIdentifier' && | ||
(value.openingElement.name.name === importMap.ActionBar || | ||
value.openingElement.name.name === styledMap.ActionBar) | ||
) | ||
.forEach(nodePath => { | ||
const attributes = nodePath.value.openingElement.attributes; | ||
|
||
if (attributes) { | ||
// remove these attributes from ActionBar | ||
nodePath.value.openingElement.attributes = attributes.filter(item => | ||
item.type === 'JSXAttribute' && | ||
item.name.type === 'JSXIdentifier' && | ||
['fixed'].includes(item.name.name) | ||
? false | ||
: true | ||
); | ||
} | ||
}); | ||
|
||
return root.toSource(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import {expectTransformFactory} from './expectTransformFactory'; | ||
import transform from '../compoundActionBar'; | ||
import {stripIndent} from 'common-tags'; | ||
|
||
const expectTransform = expectTransformFactory(transform); | ||
|
||
describe('ActionBar', () => { | ||
it('should ignore non-canvas-kit imports', () => { | ||
const input = `import ActionBar from '@workday/some-other-library'`; | ||
const expected = `import ActionBar from '@workday/some-other-library'`; | ||
|
||
expectTransform(input, expected); | ||
}); | ||
|
||
it('should replace default import', () => { | ||
const input = `import ActionBar from '@workday/canvas-kit-react/action-bar';`; | ||
const expected = `import { ActionBar } from '@workday/canvas-kit-react/action-bar';`; | ||
|
||
expectTransform(input, expected); | ||
}); | ||
|
||
it('should replace default import with a renamed export', () => { | ||
const input = `import ButtonBar from '@workday/canvas-kit-react/action-bar';`; | ||
const expected = `import { ActionBar as ButtonBar } from '@workday/canvas-kit-react/action-bar';`; | ||
|
||
expectTransform(input, expected); | ||
}); | ||
|
||
it('should ignore ActionBar from other packages', () => { | ||
const input = stripIndent` | ||
import {ActionBar} from '@workday/some-other-library' | ||
<ActionBar fixed="true" /> | ||
`; | ||
|
||
const expected = stripIndent` | ||
import {ActionBar} from '@workday/some-other-library' | ||
<ActionBar fixed="true" /> | ||
`; | ||
|
||
expectTransform(input, expected); | ||
}); | ||
|
||
it('should remove fixed prop from ActionBar component', () => { | ||
const input = stripIndent` | ||
import {ActionBar} from '@workday/canvas-kit-react/action-bar'; | ||
<ActionBar fixed="true" /> | ||
`; | ||
|
||
const expected = stripIndent` | ||
import {ActionBar} from '@workday/canvas-kit-react/action-bar'; | ||
<ActionBar /> | ||
`; | ||
|
||
expectTransform(input, expected); | ||
}); | ||
|
||
it('should remove fixed prop from ActionBar component imported from main package', () => { | ||
const input = stripIndent` | ||
import {ActionBar} from '@workday/canvas-kit-react'; | ||
<ActionBar fixed="true" /> | ||
`; | ||
|
||
const expected = stripIndent` | ||
import {ActionBar} from '@workday/canvas-kit-react'; | ||
<ActionBar /> | ||
`; | ||
|
||
expectTransform(input, expected); | ||
}); | ||
|
||
it('should replace default import and remove fixed prop', () => { | ||
const input = stripIndent` | ||
import ActionBar from '@workday/canvas-kit-react/action-bar'; | ||
<ActionBar fixed="true" /> | ||
`; | ||
|
||
const expected = stripIndent` | ||
import { ActionBar } from '@workday/canvas-kit-react/action-bar'; | ||
<ActionBar /> | ||
`; | ||
|
||
expectTransform(input, expected); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,6 @@ | ||
# Canvas Kit Action Bar | ||
# Canvas Kit React Action Bar | ||
|
||
Full width toolbar fixed to bottom of screen. | ||
`ActionBar` contains primary and secondary actions related to a page or task. | ||
|
||
Although not required, [buttons](../../button/react) are often used in in action bars. The primary | ||
action button should be left aligned followed by secondary buttons. The primary button is on the | ||
right only in task orchestration and on mobile devices. | ||
|
||
## Installation | ||
|
||
```sh | ||
yarn add @workday/canvas-kit-react | ||
``` | ||
|
||
## Usage | ||
|
||
```tsx | ||
import * as React from 'react'; | ||
import {PrimaryButton, SecondaryButton} from '@workday/canvas-kit-react/button'; | ||
import {ActionBar} from '@workday/canvas-kit-react/action-bar'; | ||
|
||
<ActionBar> | ||
<PrimaryButton variant="primary">Button</PrimaryButton> | ||
<SecondaryButton>Button</SecondaryButton> | ||
<SecondaryButton>Button</SecondaryButton> | ||
</ActionBar>; | ||
``` | ||
|
||
## Static Properties | ||
|
||
> None | ||
## Component Props | ||
|
||
### Required | ||
|
||
> None | ||
### Optional | ||
|
||
#### `fixed: boolean` | ||
|
||
> Fixes the toolbar to the bottom of the window (uses `position: fixed`) | ||
## Responsive Behavior | ||
|
||
At 575px, responsive styles will take effect: | ||
|
||
- Applies a flex box to the buttons | ||
- Makes single-button groups full width | ||
- All buttons will become the same width (`flex: 1`). | ||
- Button order will become reversed, making left-aligned primary buttons right-aligned. | ||
|
||
> When on a mobile form factor, the button placement should flip to have the primary button on the | ||
> far right. | ||
For more detailed information on this component, please refer to the | ||
[storybook documentation](https://workday.github.io/canvas-kit/?path=/docs/components-buttons-action-bar-react--basic) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1 @@ | ||
import ActionBar from './lib/ActionBar'; | ||
|
||
export default ActionBar; | ||
export {ActionBar}; | ||
export * from './lib/ActionBar'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,37 @@ | ||
import * as React from 'react'; | ||
import {styled} from '@workday/canvas-kit-react/common'; | ||
import {colors, commonColors, space, CSSProperties} from '@workday/canvas-kit-react/tokens'; | ||
import {createComponent, styled, StyledType} from '@workday/canvas-kit-react/common'; | ||
import {commonColors, colors, space} from '@workday/canvas-kit-react/tokens'; | ||
import {HStack, HStackProps, StackSpacing} from '@workday/canvas-kit-labs-react'; | ||
|
||
export interface ActionBarProps extends React.HTMLAttributes<HTMLDivElement> { | ||
/** | ||
* If true, fix the ActionBar to the bottom of the screen. | ||
* @default false | ||
*/ | ||
fixed?: boolean; | ||
interface ActionBarProps extends Omit<HStackProps, 'spacing'> { | ||
spacing?: StackSpacing; | ||
} | ||
|
||
function getFixedStyles(fixed = false): CSSProperties { | ||
return fixed | ||
? { | ||
position: 'fixed', | ||
left: 0, | ||
bottom: 0, | ||
right: 0, | ||
} | ||
: {}; | ||
} | ||
|
||
const ActionBarContainer = styled('div')<ActionBarProps>( | ||
{ | ||
borderTop: `solid 1px ${colors.soap400}`, | ||
background: commonColors.background, | ||
const ResponsiveHStack = styled(HStack)<HStackProps & StyledType>(({theme}) => ({ | ||
[theme.canvas.breakpoints.down('s')]: { | ||
padding: space.s, | ||
boxShadow: '0 -2px 4px rgba(0, 0, 0, 0.08)', | ||
}, | ||
({fixed, theme}) => { | ||
return { | ||
...getFixedStyles(fixed), | ||
[theme.canvas.breakpoints.down('s')]: { | ||
padding: space.xxs, | ||
}, | ||
}; | ||
} | ||
); | ||
|
||
const ChildrenContainer = styled('div')( | ||
{ | ||
display: 'inline-block', | ||
padding: `0 ${space.m}`, | ||
'*:not(:first-of-type)': { | ||
marginLeft: space.s, | ||
'> *:not(div)': { | ||
flex: 1, | ||
}, | ||
}, | ||
({theme}) => ({ | ||
[theme.canvas.breakpoints.down('s')]: { | ||
display: 'flex', | ||
padding: space.xxs, | ||
justifyContent: 'center', | ||
flexDirection: 'row-reverse', | ||
'> *': { | ||
flex: 1, | ||
'&:not(:first-of-type)': { | ||
marginRight: space.s, | ||
marginLeft: 0, | ||
}, | ||
}, | ||
}, | ||
}) | ||
); | ||
|
||
export default class ActionBar extends React.Component<ActionBarProps> { | ||
public render() { | ||
const {fixed, children, ...elemProps} = this.props; | ||
})); | ||
|
||
return ( | ||
<ActionBarContainer {...elemProps} fixed={fixed}> | ||
<ChildrenContainer>{children}</ChildrenContainer> | ||
</ActionBarContainer> | ||
); | ||
} | ||
} | ||
export const ActionBar = createComponent('div')({ | ||
displayName: 'ActionBar', | ||
Component: (elemProps: ActionBarProps, ref, Element) => ( | ||
<ResponsiveHStack | ||
ref={ref} | ||
as={Element} | ||
spacing="s" | ||
depth={1} | ||
background={commonColors.background} | ||
borderTop={`solid 1px ${colors.soap400}`} | ||
padding={`${space.s} ${space.xl} `} | ||
position="fixed" | ||
bottom={0} | ||
left={0} | ||
right={0} | ||
{...elemProps} | ||
/> | ||
), | ||
}); |
Oops, something went wrong.