Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RAC-225: DSM checkbox #12686

Merged
merged 23 commits into from Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7d090c8
RAC-225: first version of checkbox
StevenVAIDIE Sep 7, 2020
731afc0
RAC-225: feat improve Checkbox component
StevenVAIDIE Sep 7, 2020
c582167
RAC-225: feat add unit and visual tests
StevenVAIDIE Sep 7, 2020
8605d2a
Update visual tests for branch RAC-225 (#12690)
github-actions[bot] Sep 7, 2020
a29f377
RAC-225: expose component outside project
StevenVAIDIE Sep 7, 2020
6f1f795
RAC-225: fix Preview and Props are deprecated => https://github.com/s…
StevenVAIDIE Sep 7, 2020
7519c1f
RAC-225: add theme notion to avoid have color hex directly into compo…
StevenVAIDIE Sep 7, 2020
ca280b6
RAC-225: fix review
StevenVAIDIE Sep 7, 2020
07967ac
RAC-225: fix unit tests
StevenVAIDIE Sep 8, 2020
c4e3ecf
RAC-225: move upload atifact before update visual tests because updat…
StevenVAIDIE Sep 9, 2020
8eab1ad
RAC-225: fix indent
StevenVAIDIE Sep 9, 2020
69cf81b
relaunch actions
StevenVAIDIE Sep 9, 2020
9f14582
relaunch actions
StevenVAIDIE Sep 9, 2020
cfc7d91
remove lint to show visual regression
StevenVAIDIE Sep 10, 2020
213b70d
Update visual tests for branch RAC-225 (#12711)
github-actions[bot] Sep 10, 2020
65da7fe
RAC-225: feat add animation
StevenVAIDIE Sep 10, 2020
87aba67
feat: add missing animations
StevenVAIDIE Sep 11, 2020
4d99cdb
RAC-225: fix missing prettier on tsx files
StevenVAIDIE Sep 11, 2020
b9b0677
RAC-225: use theme from RAC-223
StevenVAIDIE Sep 11, 2020
dbc53f3
Update visual tests for branch RAC-225 (#12719)
github-actions[bot] Sep 11, 2020
87e8548
RAC-225: add usage on checkbox page from notion
StevenVAIDIE Sep 11, 2020
728dcc5
RAC-225: fix uncheck colors on notion are not the right color
StevenVAIDIE Sep 11, 2020
f7f2821
Update visual tests for branch RAC-225 (#12720)
github-actions[bot] Sep 11, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/dsm-test.yml
Expand Up @@ -25,6 +25,12 @@ jobs:
run: cd akeneo-design-system && yarn test:visual:run
id: visual_test_execution

- uses: actions/upload-artifact@v2
if: failure()
with:
name: test-folder
path: akeneo-design-system/src

# Update visual by doing a pull request
- name: Update visual tests
if: failure() && steps.visual_test_execution.outcome == 'failure'
Expand Down Expand Up @@ -74,9 +80,3 @@ jobs:
A new version of the staging env has been deployed :tada:
Check it out here: https://samirboulil.github.io/dsm/${{ github.event.number }}/
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/upload-artifact@v2
if: failure()
with:
name: test-folder
path: akeneo-design-system/src
3 changes: 2 additions & 1 deletion akeneo-design-system/.storybook/main.js
Expand Up @@ -5,6 +5,7 @@ module.exports = {
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials"
"@storybook/addon-essentials",
"themeprovider-storybook/register"
]
}
10 changes: 9 additions & 1 deletion akeneo-design-system/.storybook/preview.tsx
@@ -1,9 +1,17 @@
import React from 'react';
import {addDecorator} from '@storybook/react';
import {addDecorator, addParameters} from '@storybook/react';
import { withThemesProvider } from "themeprovider-storybook";
import {StoryStyle} from '../src/shared/global';
import {pimTheme} from '../src/theme/pim';

addDecorator(story => (
<>
<StoryStyle>{story()}</StoryStyle>
</>
));

addDecorator(withThemesProvider([pimTheme]))

addParameters({
viewMode: 'docs',
});
7 changes: 4 additions & 3 deletions akeneo-design-system/package.json
Expand Up @@ -15,8 +15,8 @@
"test:visual:run": "jest --config ./jest.visual.config.js",
"test:unit:watch": "jest --watch",
"eslint": "eslint --config .eslintrc.json --quiet src/**/*.tsx",
"lint:fix": "prettier --config .prettierrc.json --parser typescript --write \"./src/**/*.ts\";",
"lint:check": "prettier --config .prettierrc.json --parser typescript --check \"./src/**/*.ts\" && eslint --config .eslintrc.json --quiet src/**/*.{ts,tsx}",
"lint:fix": "prettier --config .prettierrc.json --parser typescript --write \"./src/**/*.{ts,tsx}\";",
"lint:check": "prettier --config .prettierrc.json --parser typescript --check \"./src/**/*.{ts,tsx}\" && eslint --config .eslintrc.json --quiet src/**/*.{ts,tsx}",
"storybook:start": "start-storybook -p 6006",
"storybook:ci": "start-storybook -p 6006 --ci",
"storybook:build": "build-storybook",
Expand All @@ -41,7 +41,7 @@
"@storybook/addon-actions": "^6.0.4",
"@storybook/addon-essentials": "^6.0.4",
"@storybook/addon-links": "^6.0.4",
"@storybook/react": "^6.0.4",
"@storybook/react": "^6.0.21",
StevenVAIDIE marked this conversation as resolved.
Show resolved Hide resolved
"@testing-library/jest-dom": "^5.11.3",
"@testing-library/react": "^10.4.8",
"@types/expect-puppeteer": "^4.4.3",
Expand All @@ -67,6 +67,7 @@
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"themeprovider-storybook": "^1.6.2",
"ts-jest": "^26.2.0",
"typescript": "^4.0.2"
}
Expand Down
87 changes: 87 additions & 0 deletions akeneo-design-system/src/components/Checkbox/Checkbox.stories.mdx
@@ -0,0 +1,87 @@
import { useState } from 'react';
import { Meta, Story, ArgsTable, Canvas } from '@storybook/addon-docs/blocks';
import { action } from "@storybook/addon-actions";
import { Checkbox } from "./Checkbox.tsx";

<Meta
title="Components/Checkbox"
component={Checkbox}
argTypes={{
readOnly: {control: {type: 'boolean'}},
checked: {control: {type: 'boolean'}},
label: {control: {type: 'text'}},
onChange: {action: 'Checkbox component onChange'},
}}
args={{
label: 'Checkbox'
}}
/>

# Checkbox

## Usage
The checkboxes are applied when users can select all, several, or none of the options from a given list.

### Checkbox states
The checkbox control allows three states: selected, unselected, and undetermined. The undetermined state comes into play when the checkbox contains a sublist of selections, some of which are selected, and others aren't.

Users must be able to check the box by clicking directly on the box or by clicking on its label.

### Content
#### Title

If necessary, a header can accompany a set of checkboxes to provide more context or clarity.

#### Labels

Always use clear and concise labels for the checkboxes. The labels appears on the right of the checkboxes.

## Playground

Use this playground to test the checkbox component

<Canvas>
<Story name="Standard">
{(args) => {
return <Checkbox {...args}/>;
}}
</Story>
</Canvas>

<ArgsTable story="Standard" />

## Variation on state
<Story name="State">
{(args) => {
return (
<>
<Checkbox {...args} checked={true} label={'Checkbox checked'}/>
<Checkbox {...args} checked={false} undetermined={true} label={'Checkbox undetermined'}/>
<Checkbox {...args} checked={false} label={'Checkbox unchecked'}/>
</>
)
}}
</Story>

## Variation on disabled
<Story name="Disabled">
{(args) => {
return (
<>
<Checkbox {...args} readOnly={true} checked={true} label={'Checked disabled'}/>
<Checkbox {...args} readOnly={true} checked={false} undetermined={true} label={'Undetermined disabled'}/>
<Checkbox {...args} readOnly={true} checked={false} label={'Unchecked disabled'}/>
</>
)
}}
</Story>

## Animation
<Story name="Animation">
{(args) => {
const [checked, setChecked] = useState(true);
return (
<Checkbox {...args} checked={checked} onChange={(newCheck) => setChecked(newCheck)}/>
)
}}
</Story>
131 changes: 131 additions & 0 deletions akeneo-design-system/src/components/Checkbox/Checkbox.tsx
@@ -0,0 +1,131 @@
import React from 'react';
import styled, {css, keyframes} from 'styled-components';
import {AkeneoThemedProps, getColor} from '../../theme';
import {CheckIcon, PartialCheckIcon} from '../../icons';

const checkTick = keyframes`
to {
stroke-dashoffset: 0;
}
`;

const uncheckTick = keyframes`
to {
stroke-dashoffset: 20px;
}
`;

const Container = styled.div`
display: flex;
`;

const TickIcon = styled(CheckIcon)`
animation: ${uncheckTick} 0.2s ease-in forwards;
opacity: 0;
stroke-dasharray: 0px;
stroke-dashoffset: 0;
transition-delay: 0.2s;
transition: opacity 0.1s ease-out;
`;

const CheckboxContainer = styled.div<{checked: boolean; readOnly: boolean} & AkeneoThemedProps>`
background-color: transparent;
height: 20px;
width: 20px;
border: 1px solid ${getColor('grey80')};
border-radius: 3px;
outline: none;
background-color: ${getColor('grey20')};

${(props) =>
props.checked &&
css`
background-color: ${getColor('blue100')};
${TickIcon} {
animation-delay: 0.2s;
animation: ${checkTick} 0.2s ease-out forwards;
stroke-dashoffset: 20px;
opacity: 1;
transition-delay: 0s;
}
`}

${(props) =>
props.checked &&
props.readOnly &&
css`
background-color: ${getColor('blue20')};
border-color: ${getColor('blue40')};
`}

${(props) =>
!props.checked &&
props.readOnly &&
css`
background-color: ${getColor('grey60')};
border-color: ${getColor('grey100')};
`}
`;

const LabelContainer = styled.div<{readOnly: boolean} & AkeneoThemedProps>`
color: ${getColor('grey140')};
font-weight: 400;
font-size: 15px;
padding-left: 10px;

${(props) =>
props.readOnly &&
css`
color: ${getColor('grey100')};
`}
`;

type CheckboxProps = {
/**
* State of the Checkbox
*/
checked: boolean;

/**
* Displays the value of the input, but does not allow changes.s
*/
readOnly?: boolean;

/**
* The undetermined state comes into play when the checkbox contains a sublist of selections,
* some of which are selected, and others aren't.
*/
undetermined?: boolean;

/**
* Provide a description of the Checkbox, the label appears on the right of the checkboxes.
*/
label?: string;

/**
* The handler called when clicking on Checkbox
*/
onChange?: (value: boolean) => void;
};

/**
* The checkboxes are applied when users can select all, several, or none of the options from a given list.
*/
const Checkbox = ({label, checked, onChange, undetermined = false, readOnly = false}: CheckboxProps) => {
if (undefined === onChange && false === readOnly) {
throw new Error('A Checkbox element expect an onChange attribute if not readOnly');
}

const handleChange = () => onChange && !readOnly && onChange(!checked);

return (
<Container onClick={handleChange}>
<CheckboxContainer checked={checked || undetermined} readOnly={readOnly}>
{undetermined ? <PartialCheckIcon height={20} width={20} /> : <TickIcon height={20} width={20} />}
</CheckboxContainer>
{label ? <LabelContainer readOnly={readOnly}>{label}</LabelContainer> : null}
</Container>
);
};

export {Checkbox};
33 changes: 33 additions & 0 deletions akeneo-design-system/src/components/Checkbox/Checkbox.unit.tsx
@@ -0,0 +1,33 @@
import React from 'react';
import {fireEvent, render} from '../../shared/test-util';
import {Checkbox} from './Checkbox';

it('it calls onChange handler when user clicks on checkbox', () => {
const onChange = jest.fn();
const {getByText} = render(<Checkbox checked={true} onChange={onChange} label="Checkbox" />);

const checkbox = getByText('Checkbox');
fireEvent.click(checkbox);

expect(onChange).toBeCalledWith(false);
});

it('it does not call onChange handler when read-only', () => {
const onChange = jest.fn();
const {getByText} = render(<Checkbox checked={true} readOnly={true} onChange={onChange} label="Checkbox" />);

const checkbox = getByText('Checkbox');
fireEvent.click(checkbox);

expect(onChange).not.toBeCalled();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we check the checkbox is still checked ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, we didn't need to check if the checkbox is checked because it's a controlled component but I don't know @ValentinMumble what is your opinion ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as it is a controlled component there shouldn't be any internal logic that could change the checked prop so not really needed for me as well but as you wish

});

it('it cannot be instantiated without handler when not readonly', () => {
jest.spyOn(console, 'error').mockImplementation(() => {
// do nothing.
});

expect(() => {
render(<Checkbox checked={true} label="Checkbox" />);
}).toThrow('A Checkbox element expect an onChange attribute if not readOnly');
});
37 changes: 37 additions & 0 deletions akeneo-design-system/src/components/Checkbox/Checkbox.visual.tsx
@@ -0,0 +1,37 @@
import 'expect-puppeteer';

import {toMatchImageSnapshot} from 'jest-image-snapshot';

expect.extend({toMatchImageSnapshot});

describe('Checkbox visual tests', () => {
it('Renders standard checkbox component correctly', async () => {
await page.goto('http://localhost:6006/iframe.html?id=components-checkbox--standard');
const root = await page.$('#root');
if (null === root) return;

const image = await root.screenshot();

expect(image).toMatchImageSnapshot();
});

it('Renders checkbox component varying by state', async () => {
await page.goto('http://localhost:6006/iframe.html?id=components-checkbox--state');
const root = await page.$('#root');
if (null === root) return;

const image = await root.screenshot();

expect(image).toMatchImageSnapshot();
StevenVAIDIE marked this conversation as resolved.
Show resolved Hide resolved
});

it('Renders checkbox component varying on disabled', async () => {
await page.goto('http://localhost:6006/iframe.html?id=components-checkbox--disabled');
const root = await page.$('#root');
if (null === root) return;

const image = await root.screenshot();

expect(image).toMatchImageSnapshot();
});
});
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions akeneo-design-system/src/components/Dummy/Dummy.stories.mdx
@@ -1,4 +1,4 @@
import { Meta, Story, Preview, Props, ArgsTable, Canvas } from '@storybook/addon-docs/blocks';
import { Meta, Story, ArgsTable, Canvas } from '@storybook/addon-docs/blocks';
import { action } from "@storybook/addon-actions";
import { Dummy, Type } from "./Dummy.tsx";

Expand All @@ -20,13 +20,13 @@ The dummy component is a little bit dumb, but that's why it exists.

Use this playground to test the dummy component

<Preview>
<Canvas>
<Story name="Standard">
{(args) => <Dummy {...args} >Coucou</Dummy>}
</Story>
</Preview>
</Canvas>

<Props story="Standard" />
<ArgsTable story="Standard" />


## Variation on size
Expand Down