diff --git a/packages/modul-components/src/components/toggle-buttons/toggle-buttons.html b/packages/modul-components/src/components/toggle-buttons/toggle-buttons.html index e289c2bf1..a4d890219 100644 --- a/packages/modul-components/src/components/toggle-buttons/toggle-buttons.html +++ b/packages/modul-components/src/components/toggle-buttons/toggle-buttons.html @@ -1,13 +1,22 @@ -
- - {{ button.title }} - +
+
diff --git a/packages/modul-components/src/components/toggle-buttons/toggle-buttons.scss b/packages/modul-components/src/components/toggle-buttons/toggle-buttons.scss index 2c9419301..1169e8d43 100644 --- a/packages/modul-components/src/components/toggle-buttons/toggle-buttons.scss +++ b/packages/modul-components/src/components/toggle-buttons/toggle-buttons.scss @@ -2,30 +2,109 @@ @import '../button/button.scss'; .m-toggle-buttons { - display: inline-block; + display: inline-flex; - &.m--is-default { - .m-button { - border-radius: 0; + &__list { + display: inline-flex; + list-style-type: none; - &:not(:nth-last-child(1)) { - border-right: none; + &, + li { + margin: 0; + padding: 0; + } + } + + &__button { + position: relative; + transition-property: background-color, border-color, color, outline; + transition-duration: $m-transition-duration; + transition-timing-function: ease; + display: flex; + align-items: center; + border: 1px solid $m-color-interactive; + padding: 10px $m-space; + font-family: inherit; + font-size: inherit; + font-weight: $m-font-weight-semi-bold; + text-align: center; + text-decoration: none; + appearance: none; + color: $m-color-interactive; + outline-color: $m-color-interactive-darker; + outline-offset: 2px; + outline-width: 1px; + background-color: $m-color-white; + user-select: none; + overflow: hidden; + + &:not(.m--disabled) { + cursor: pointer; + + &:not([aria-checked="true"]):hover { + background-color: $m-color-information; } - &:nth-child(1) { - border-radius: $m-border-radius 0 0 $m-border-radius; + &[aria-checked="true"]:hover { + background-color: $m-color-interactive-darker; } - &:nth-last-child(1) { - border-radius: 0 $m-border-radius $m-border-radius 0; + } + + &.m--disabled { + cursor: default; + color: $m-color-disabled; + background: $m-color-white; + border-color: $m-color-disabled; + + &[aria-checked="true"] { + color: $m-color-white; + background: $m-color-disabled; } } - } - &.m--is-rounded { - .m-button { - border-radius: 25px; // magic number - margin: 0 $m-space-2xs $m-space-xs 0; + &[aria-checked="true"] { + color: $m-color-white; + background: $m-color-interactive; } } + + +} + +.m-toggle-buttons__list.m--is-default { + .m-toggle-buttons__button { + border-radius: 0; + } + + >.m--checked+.m--checked .m-toggle-buttons__button { + border-left-color: $m-color-white; + } + + > :not(:nth-last-child(1)) .m-toggle-buttons__button { + border-right: none; + } + + > :nth-child(1) .m-toggle-buttons__button { + border-radius: $m-border-radius 0 0 $m-border-radius; + } + + > :nth-last-child(1) .m-toggle-buttons__button { + border-radius: 0 $m-border-radius $m-border-radius 0; + } + +} + + +.m-toggle-buttons__list.m--is-rounded { + flex-wrap: wrap; + gap: $m-space-xs 0; + + > :not(:last-child) { + margin-right: $m-space-xs; + } + + .m-toggle-buttons__button { + border-radius: 25px; // magic number + } } diff --git a/packages/modul-components/src/components/toggle-buttons/toggle-buttons.spec.ts b/packages/modul-components/src/components/toggle-buttons/toggle-buttons.spec.ts deleted file mode 100644 index f35369bd0..000000000 --- a/packages/modul-components/src/components/toggle-buttons/toggle-buttons.spec.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { createLocalVue, mount, Wrapper } from '@vue/test-utils'; -import Vue, { VueConstructor } from 'vue'; -import { resetModulPlugins } from '../../../tests/helpers/component'; -import ModulPlugin from '../../utils/modul/modul'; -import ToggleButtonsPlugin, { MToggleButton, MToggleButtons } from './toggle-buttons'; - -let wrapper: Wrapper; -let localVue: VueConstructor; - -let buttons: MToggleButton[] = []; -let multiple: boolean; -let disabled: boolean; - -const BTN_A: MToggleButton = { id: 'a', title: 'A' }; -const BTN_B: MToggleButton = { id: 'b', title: 'B' }; -const BTN_C: MToggleButton = { id: 'c', title: 'C' }; -const BTN_D: MToggleButton = { id: 'd', title: 'D' }; -const BTN_E: MToggleButton = { id: 'e', title: 'E' }; - -const CLASS_BTN_PRIMARY: string = 'm--is-skin-primary'; -const CLASS_BTN_SECONDARY: string = 'm--is-skin-secondary'; - -const initializeWrapper: () => any = () => { - wrapper = mount(MToggleButtons, { - localVue: localVue, - propsData: { - buttons, - multiple, - disabled - } - }); -}; - -beforeEach(() => { - wrapper = undefined as any; -}); - -describe('MToggleButtons', () => { - beforeEach(() => { - resetModulPlugins(); - localVue = createLocalVue(); - localVue.use(ModulPlugin); - Vue.use(ToggleButtonsPlugin); - }); - - describe(`Given no button`, () => { - - beforeEach(() => { - initializeWrapper(); - }); - - it(`Then no buttons should be displayed`, () => { - expect(wrapper.vm.$refs.toggle).toBeTruthy(); - expect(wrapper.findAll('.m-toggle-buttons__button').length).toEqual(0); - }); - }); - - describe(`Given 5 buttons and multiple selection allow`, () => { - - beforeEach(() => { - buttons = [BTN_A, BTN_B, BTN_C, BTN_D, BTN_E]; - multiple = true; - initializeWrapper(); - }); - - it(`Then 5 buttons should be displayed`, () => { - expect(wrapper.vm.$refs.toggle).toBeTruthy(); - expect(wrapper.findAll('.m-toggle-buttons__button').length).toEqual(5); - }); - - it(`Then no button should be pressed`, () => { - expect(wrapper.findAll('.CLASS_BTN_PRIMARY').length).toEqual(0); - }); - - describe(`When the first button is clicked`, () => { - it(`Should emit a change event and prop 'button' is updated on first button`, () => { - wrapper.findAll('.m-toggle-buttons__button').at(0).trigger('click'); - expect(wrapper.emitted('change')).toEqual([[[{ ...BTN_A, pressed: true }, BTN_B, BTN_C, BTN_D, BTN_E]]]); - }); - }); - - describe(`When second button is clicked`, () => { - beforeEach(() => { - buttons = [{ ...BTN_A, pressed: true }, BTN_B, BTN_C, BTN_D, BTN_E]; - multiple = true; - initializeWrapper(); - }); - it(`Should emit a change event and prop 'buttons' is updated on 2 first buttons`, () => { - wrapper.findAll('.m-toggle-buttons__button').at(1).trigger('click'); - expect(wrapper.emitted('change')).toEqual([[[{ ...BTN_A, pressed: true }, { ...BTN_B, pressed: true }, BTN_C, BTN_D, BTN_E]]]); - }); - }); - }); - - describe(`Given 5 buttons and multiple selection not allow`, () => { - - beforeEach(() => { - buttons = [BTN_A, BTN_B, BTN_C, BTN_D, BTN_E]; - multiple = false; - initializeWrapper(); - }); - - it(`Then 5 buttons should be displayed`, () => { - expect(wrapper.vm.$refs.toggle).toBeTruthy(); - expect(wrapper.findAll('.m-toggle-buttons__button').length).toEqual(5); - }); - - it(`Then no button should be pressed`, () => { - expect(wrapper.findAll('.CLASS_BTN_PRIMARY').length).toEqual(0); - }); - - describe(`When the first button is clicked`, () => { - it(`Should emit a change event and prop 'buttons' is updated on first button`, () => { - wrapper.findAll('.m-toggle-buttons__button').at(0).trigger('click'); - expect(wrapper.emitted('change')).toEqual([[[{ ...BTN_A, pressed: true }, { ...BTN_B, pressed: false }, { ...BTN_C, pressed: false }, { ...BTN_D, pressed: false }, { ...BTN_E, pressed: false }]]]); - }); - }); - - describe(`When second button is clicked`, () => { - beforeEach(() => { - buttons = [{ ...BTN_A, pressed: true }, BTN_B, BTN_C, BTN_D, BTN_E]; - multiple = false; - initializeWrapper(); - }); - it(`Should emit a change event and prop 'button' is updated, first button is unpressed and second button is pressed`, () => { - wrapper.findAll('.m-toggle-buttons__button').at(1).trigger('click'); - expect(wrapper.emitted('change')).toEqual([[[{ ...BTN_A, pressed: false }, { ...BTN_B, pressed: true }, { ...BTN_C, pressed: false }, { ...BTN_D, pressed: false }, { ...BTN_E, pressed: false }]]]); - }); - }); - - }); - - describe(`Given toggle-button disabled`, () => { - - beforeEach(() => { - buttons = [BTN_A, BTN_B]; - multiple = false; - disabled = true; - initializeWrapper(); - }); - - it(`Then buttons should be disabled`, () => { - expect(wrapper.vm.$refs.toggle).toBeTruthy(); - expect(wrapper.findAll('.m-toggle-buttons__button').at(0).props().disabled).toBeTruthy(); - expect(wrapper.findAll('.m-toggle-buttons__button').at(1).props().disabled).toBeTruthy(); - }); - }); -}); diff --git a/packages/modul-components/src/components/toggle-buttons/toggle-buttons.ts b/packages/modul-components/src/components/toggle-buttons/toggle-buttons.ts index 4660e8243..d96eae591 100644 --- a/packages/modul-components/src/components/toggle-buttons/toggle-buttons.ts +++ b/packages/modul-components/src/components/toggle-buttons/toggle-buttons.ts @@ -1,7 +1,6 @@ import Vue, { PluginObject } from 'vue'; import Component from 'vue-class-component'; import { Emit, Model, Prop } from 'vue-property-decorator'; -import { MButton, MButtonSkin } from '../button/button'; import { TOGGLE_BUTTONS_NAME } from '../component-names'; import WithRender from './toggle-buttons.html?style=./toggle-buttons.scss'; @@ -17,16 +16,15 @@ export enum MToggleButtonSkin { } @WithRender -@Component({ - components: { - MButton - } -}) +@Component export class MToggleButtons extends Vue { @Model('change') @Prop({ default: () => [] }) public readonly buttons: MToggleButton[]; + @Prop({ required: true }) + public readonly ariaLabel?: string; + @Prop({ default: true }) public readonly multiple: boolean; @@ -41,28 +39,27 @@ export class MToggleButtons extends Vue { }) public readonly skin: MToggleButtonSkin; + public get skinButtons(): { [key: string]: boolean } { + return { + 'm--is-default': this.skin === MToggleButtonSkin.SQUARED, + 'm--is-rounded': this.skin === MToggleButtonSkin.ROUNDED + }; + } + + @Emit('click') + public emitClick(_button: MToggleButton): void { } + public toggle(button: MToggleButton): void { + if (this.disabled) { return; } + this.$emit('change', this.buttons.map(b => b.id !== button.id ? this.multiple ? b : { ...b, pressed: false } : { ...b, pressed: !b.pressed } )); - this.onClick({ ...button, pressed: !button.pressed }); - } - - @Emit('click') - private onClick(button: MToggleButton): void { } - - public getSkin(button: MToggleButton): string { - return !button.pressed ? MButtonSkin.Secondary : MButtonSkin.Primary; + this.emitClick({ ...button, pressed: !button.pressed }); } - get skinButtons(): { [key: string]: boolean } { - return { - 'm--is-default': this.skin === MToggleButtonSkin.SQUARED, - 'm--is-rounded': this.skin === MToggleButtonSkin.ROUNDED - }; - } } const ToggleButtonsPlugin: PluginObject = { diff --git a/src/storybook/src/modul-components/components/toggle-buttons/__snapshots__/toggle-buttons.stories.ts.snap b/src/storybook/src/modul-components/components/toggle-buttons/__snapshots__/toggle-buttons.stories.ts.snap deleted file mode 100644 index aae284f48..000000000 --- a/src/storybook/src/modul-components/components/toggle-buttons/__snapshots__/toggle-buttons.stories.ts.snap +++ /dev/null @@ -1,635 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Storyshots modul-components|m-toggle-buttons default 1`] = ` -
- - - - -
-`; - -exports[`Storyshots modul-components|m-toggle-buttons disabled 1`] = ` -
- - - - -
-`; - -exports[`Storyshots modul-components|m-toggle-buttons multiple selection 1`] = ` -
- - - - -
-`; - -exports[`Storyshots modul-components|m-toggle-buttons single selection 1`] = ` -
- - - - -
-`; - -exports[`Storyshots modul-components|m-toggle-buttons skin rounded 1`] = ` -
- - - - -
-`; - -exports[`Storyshots modul-components|m-toggle-buttons with slots 1`] = ` -
- - - - -
-`; diff --git a/src/storybook/src/modul-components/components/toggle-buttons/toggle-buttons.stories.ts b/src/storybook/src/modul-components/components/toggle-buttons/toggle-buttons.stories.ts index 68fdc8914..9c07b8926 100644 --- a/src/storybook/src/modul-components/components/toggle-buttons/toggle-buttons.stories.ts +++ b/src/storybook/src/modul-components/components/toggle-buttons/toggle-buttons.stories.ts @@ -3,7 +3,6 @@ import { TOGGLE_BUTTONS_NAME } from '@ulaval/modul-components/dist/components/co import { MToggleButton, MToggleButtonSkin } from '@ulaval/modul-components/dist/components/toggle-buttons/toggle-buttons'; import { modulComponentsHierarchyRootSeparator } from '../../../utils'; - const JUNE: MToggleButton = { id: 'june', title: 'June' }; const JULY: MToggleButton = { id: 'july', title: 'July' }; const AUGUST: MToggleButton = { id: 'august', title: 'August' }; @@ -21,7 +20,7 @@ storiesOf(`${modulComponentsHierarchyRootSeparator}${TOGGLE_BUTTONS_NAME}`, modu data: () => ({ buttons: monthsDefault }), - template: '' + template: '' })) .add('skin rounded', () => ({ props: { @@ -32,26 +31,26 @@ storiesOf(`${modulComponentsHierarchyRootSeparator}${TOGGLE_BUTTONS_NAME}`, modu data: () => ({ buttons: monthsDefault }), - template: '' + template: '' })) .add('multiple selection', () => ({ data: () => ({ buttons: monthsMultipleSelection }), - template: '' + template: '' })) .add('single selection', () => ({ data: () => ({ buttons: monthsSingleSelection, multiple: false }), - template: '' + template: '' })) .add('with slots', () => ({ data: () => ({ buttons: monthsSingleSelection }), - template: ` + template: ` @@ -62,5 +61,5 @@ storiesOf(`${modulComponentsHierarchyRootSeparator}${TOGGLE_BUTTONS_NAME}`, modu buttons: monthsSingleSelection, disabled: true }), - template: '' + template: '' }));