Skip to content

Commit

Permalink
feat: add cv-content-switcher
Browse files Browse the repository at this point in the history
  • Loading branch information
davidnixon committed Nov 28, 2022
1 parent 2aa1898 commit e289a6c
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 52 deletions.
2 changes: 1 addition & 1 deletion src/components/CvComboBox/__tests__/CvComboBox.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('CvComboBox', () => {
const wrapper = result.container.querySelector('.bx--list-box__wrapper');
expect(wrapper.getAttribute('aria-label')).toBe(ariaLabel);

await result.rerender({ ...props, invalidMessage: '' });
await result.rerender({ invalidMessage: '' });
await result.findByText(props.helperText);
});

Expand Down
199 changes: 172 additions & 27 deletions src/components/CvContentSwitcher/CvContentSwitcher.stories.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { sbCompPrefix, storyParametersObject } from '@/global/storybook-utils';

import { CvContentSwitcher } from '.';
import CvContentSwitcherButton from './CvContentSwitcherButton';
import CvContentSwitcherContent from './CvContentSwitcherContent';
import { IbmSecurity20 } from '@carbon/icons-vue';
import { action } from '@storybook/addon-actions';
import { ref } from 'vue';

const initArgs = {
title: 'Combo Box title',
helperText: 'Combobox helper text',
light: false,
selectedIndex: 0,
};

export default {
Expand All @@ -15,50 +17,193 @@ export default {
parameters: {
a11y: {
config: {
rules: [
{
// The autocomplete rule will not run based on the CSS selector provided
id: 'aria-input-field-name',
selector: '.bx--combo-box',
},
],
rules: [],
},
options: {},
manual: true,
},
controls: { exclude: ['default', 'selected'] },
},
argTypes: {
title: { type: String, description: 'Combo Box title' },
options: { type: Array, description: 'Combo Box options' },
light: {
description: 'light theme',
table: {
category: 'props',
},
},
size: {
type: 'select',
options: ['sm', 'md', 'xl'],
},
},
};

const template = `
<div style='width: 18.75rem'>
<cv-content-switcher aria-label='Choose a fruit' v-bind='args' />
<div>
<cv-content-switcher aria-label='Choose content' v-bind='args' @selected="onSelected">
<cv-content-switcher-button owner-id="csb-1" :selected="selectedIndex === 0">Button Name 1</cv-content-switcher-button>
<cv-content-switcher-button owner-id="csb-2" :selected="selectedIndex === 1">Button Name 2</cv-content-switcher-button>
<cv-content-switcher-button :icon="iconSample" owner-id="csb-3" :selected="selectedIndex === 2">Button Name 3</cv-content-switcher-button>
</cv-content-switcher>
<section style="margin: 10px 0;">
<cv-content-switcher-content owner-id="csb-1">
<p>This is the content for option 1</p>
</cv-content-switcher-content>
<cv-content-switcher-content owner-id="csb-2">
<p>This is the content for option 2</p>
</cv-content-switcher-content>
<cv-content-switcher-content owner-id="csb-2" >
<p>This is more content for option 2</p>
</cv-content-switcher-content>
<cv-content-switcher-content owner-id="csb-3">
<p>This is the content for option 3</p>
</cv-content-switcher-content>
</section>
</div>
<script>
import CvContentSwitcher from './CvContentSwitcher';
export default {
components: { CvContentSwitcher }
};
</script>`;
`;
const Template = args => {
return {
components: { CvContentSwitcher },
components: {
CvContentSwitcher,
CvContentSwitcherButton,
CvContentSwitcherContent,
},
setup: () => ({
args,
onChange: action('change'),
onFilter: action('filter'),
onSelected: action('selected'),
selectedIndex: args.selectedIndex,
iconSample: IbmSecurity20,
}),
template,
};
};

export const Default = Template.bind({});
Default.args = initArgs;
Default.parameters = storyParametersObject(
Default.parameters,
template,
Default.args
// Default.parameters = storyParametersObject(
// Default.parameters,
// template,
// Default.args
// );
// Default.parameters.controls = { exclude: ['default', 'selected'] };

/**
* Direct DOM
*/
const templateDom = `
<div>
<cv-content-switcher aria-label='Choose content' v-bind='args' @selected="onSelected">
<cv-content-switcher-button content-selector=".content-1" :selected="selectedIndex === 0">Button Name 1</cv-content-switcher-button>
<cv-content-switcher-button content-selector=".content-2" :selected="selectedIndex === 1">Button Name 2</cv-content-switcher-button>
<cv-content-switcher-button :icon="iconSample" content-selector=".content-3" :selected="selectedIndex === 2">Button Name 3</cv-content-switcher-button>
</cv-content-switcher>
<section style="margin: 10px 0;">
<div class="content-1">
<p>This is the DOM content for option 1</p>
</div>
<div class="content-2">
<p>This is the DOM content for option 2</p>
</div>
<div class="content-2" >
<p>This is more DOM content for option 2</p>
</div>
<div class="content-3">
<p>This is the DOM content for option 3</p>
</div>
</section>
</div>
`;
const TemplateDom = args => {
return {
components: {
CvContentSwitcher,
CvContentSwitcherButton,
CvContentSwitcherContent,
},
setup: () => ({
args,
onSelected: action('selected'),
selectedIndex: args.selectedIndex,
iconSample: IbmSecurity20,
}),
template: templateDom,
};
};
export const directDom = TemplateDom.bind({});
directDom.args = initArgs;
directDom.parameters = storyParametersObject(
directDom.parameters,
templateDom,
directDom.args
);
directDom.parameters.controls = { exclude: ['default', 'selected'] };

/**
* Multiple switchers
*/
const templateMultiple = `
<div>
<cv-content-switcher aria-label='Choose content' v-bind='args' @selected="onSelected" id="star-wars">
<cv-content-switcher-button parentSwitcher="star-wars" owner-id="episode-1" :selected="selectedIndex === 0">Episode 1</cv-content-switcher-button>
<cv-content-switcher-button parentSwitcher="star-wars" owner-id="episode-2" :selected="selectedIndex === 1">Episode 2</cv-content-switcher-button>
<cv-content-switcher-button parentSwitcher="star-wars" :icon="iconSample" owner-id="episode-3" :selected="selectedIndex === 2">Episode 3</cv-content-switcher-button>
</cv-content-switcher>
<section style="margin: 10px 0;">
<cv-content-switcher-content parentSwitcher="star-wars" owner-id="episode-1">
<p>Padme</p>
</cv-content-switcher-content>
<cv-content-switcher-content parentSwitcher="star-wars" owner-id="episode-2">
<p>Anakin</p>
</cv-content-switcher-content>
<cv-content-switcher-content parentSwitcher="star-wars" owner-id="episode-2" >
<p>Zam</p>
</cv-content-switcher-content>
<cv-content-switcher-content parentSwitcher="star-wars" owner-id="episode-3">
<p>Yoda</p>
</cv-content-switcher-content>
</section>
<cv-content-switcher aria-label='Choose content' v-bind='args' @selected="onSelected" id="LotR">
<cv-content-switcher-button parentSwitcher="LotR" owner-id="book-1" :selected="selectedIndex === 0">Book 1</cv-content-switcher-button>
<cv-content-switcher-button parentSwitcher="LotR" owner-id="book-2" :selected="selectedIndex === 1">Book 2</cv-content-switcher-button>
<cv-content-switcher-button parentSwitcher="LotR" :icon="iconSample" owner-id="book-3" :selected="selectedIndex === 2">Book 3</cv-content-switcher-button>
</cv-content-switcher>
<section style="margin: 10px 0;">
<cv-content-switcher-content parentSwitcher="LotR" owner-id="book-1">
<p>Bilbo</p>
</cv-content-switcher-content>
<cv-content-switcher-content parentSwitcher="LotR" owner-id="book-2">
<p>Frodo</p>
</cv-content-switcher-content>
<cv-content-switcher-content parentSwitcher="LotR" owner-id="book-2" >
<p>Sauron</p>
</cv-content-switcher-content>
<cv-content-switcher-content parentSwitcher="LotR" owner-id="book-3">
<p>Gandalf</p>
</cv-content-switcher-content>
</section>
</div>
`;
const TemplateMultiple = args => {
return {
components: {
CvContentSwitcher,
CvContentSwitcherButton,
CvContentSwitcherContent,
},
setup: () => ({
args,
onSelected: action('selected'),
selectedIndex: args.selectedIndex,
iconSample: IbmSecurity20,
}),
template: templateMultiple,
};
};
export const multipleSwitchers = TemplateMultiple.bind({});
multipleSwitchers.args = initArgs;
multipleSwitchers.parameters = storyParametersObject(
multipleSwitchers.parameters,
templateMultiple,
multipleSwitchers.args
);
multipleSwitchers.parameters.controls = { exclude: ['default', 'selected'] };
67 changes: 59 additions & 8 deletions src/components/CvContentSwitcher/CvContentSwitcher.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,64 @@
<template>

<div
:class="[
`cv-content-switcher ${carbonPrefix}--content-switcher`,
{
[`${carbonPrefix}--content-switcher--light`]: isLight,
[`${carbonPrefix}--content-switcher--${size || 'md'}`]: size || 'md',
},
]"
role="tablist"
:id="cvId"
>
<slot></slot>
</div>
</template>

<script>
export default {
name: "CvContentSwitcher"
}
</script>
<script setup>
import { carbonPrefix } from '@/global/settings';
import { props as propsCvId, useCvId } from '@/use/cvId';
import { useIsLight, props as propsTheme } from '../../use/cvTheme';
import { onUnmounted, watch } from 'vue';
import store from './cvContentSwitcherStore';
const emit = defineEmits(['selected']);
<style scoped>
const props = defineProps({
size: {
type: String,
default: undefined,
validator: val => ['', 'md', 'sm', 'xl'].includes(val),
},
...propsTheme,
...propsCvId,
});
const cvId = useCvId(props, true);
const isLight = useIsLight(props);
store.addParent(cvId.value);
store.setContentSelected('global', undefined);
onUnmounted(() => {
store.removeParent(cvId.value);
});
watch(
() => store.state.global.latest,
(val, prev) => {
// if previous is not set, do not emit. This mimics the current v2 behaviour where the initial content does not generate an event
if (prev) emit('selected', val);
}
);
let ourLatest = undefined;
watch(
() => store.state[cvId.value],
val => {
if (ourLatest !== val?.latest) {
// if ourLatest is not set, do not emit. This mimics the current v2 behaviour where the initial content does not generate an event
if (ourLatest) emit('selected', val?.latest);
ourLatest = val?.latest;
}
},
{ deep: true }
);
</script>
</style>
<style lang="scss"></style>
Loading

0 comments on commit e289a6c

Please sign in to comment.