@citizenplane/pimp 8.11.2 → 8.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +0 -2
  2. package/dist/pimp.es.js +1146 -1139
  3. package/dist/pimp.umd.js +3 -3
  4. package/dist/style.css +1 -1
  5. package/package.json +26 -4
  6. package/src/README.md +0 -25
  7. package/src/assets/styles/base/_base.scss +1 -5
  8. package/src/assets/styles/utilities/_index.scss +19 -0
  9. package/src/components/core/BaseInputLabel.vue +0 -2
  10. package/src/components/date-pickers/CpDate.vue +8 -5
  11. package/src/components/inputs/CpInput.vue +5 -16
  12. package/src/components/inputs/CpTextarea.vue +4 -6
  13. package/src/components/selects/CpSelect.vue +5 -5
  14. package/src/stories/BaseInputLabel.stories.ts +35 -0
  15. package/src/stories/CpAlert.stories.ts +90 -0
  16. package/src/stories/CpBadge.stories.ts +158 -0
  17. package/src/stories/CpButton.stories.ts +134 -0
  18. package/src/stories/CpCheckbox.stories.ts +184 -0
  19. package/src/stories/CpDate.stories.ts +110 -0
  20. package/src/stories/CpDatepicker.stories.ts +162 -0
  21. package/src/stories/CpDialog.stories.ts +53 -0
  22. package/src/stories/CpHeading.stories.ts +77 -0
  23. package/src/stories/CpIcon.stories.ts +79 -0
  24. package/src/stories/CpInput.stories.ts +155 -0
  25. package/src/stories/CpLoader.stories.ts +29 -0
  26. package/src/stories/CpRadio.stories.ts +139 -0
  27. package/src/stories/CpSelect.stories.ts +147 -0
  28. package/src/stories/CpSelectMenu.stories.ts +132 -0
  29. package/src/stories/CpSwitch.stories.ts +137 -0
  30. package/src/stories/CpTable.stories.ts +192 -0
  31. package/src/stories/CpTableEmptyState.stories.ts +34 -0
  32. package/src/stories/CpTextarea.stories.ts +112 -0
  33. package/src/stories/CpToaster.stories.ts +147 -0
  34. package/src/stories/CpTooltip.stories.ts +101 -0
  35. package/src/stories/TransitionExpand.stories.ts +85 -0
  36. package/vitest.workspace.js +31 -0
  37. package/src/App.vue +0 -110
  38. package/src/components/core/playground-sections/SectionAtomicElements.vue +0 -83
  39. package/src/components/core/playground-sections/SectionButtons.vue +0 -142
  40. package/src/components/core/playground-sections/SectionContainer.vue +0 -50
  41. package/src/components/core/playground-sections/SectionDatePickers.vue +0 -160
  42. package/src/components/core/playground-sections/SectionDialog.vue +0 -47
  43. package/src/components/core/playground-sections/SectionFeedbackIndicators.vue +0 -47
  44. package/src/components/core/playground-sections/SectionInputs.vue +0 -46
  45. package/src/components/core/playground-sections/SectionListsAndTables.vue +0 -268
  46. package/src/components/core/playground-sections/SectionSelectMenus.vue +0 -98
  47. package/src/components/core/playground-sections/SectionSelects.vue +0 -120
  48. package/src/components/core/playground-sections/SectionSimpleInputs.vue +0 -305
  49. package/src/components/core/playground-sections/SectionToasters.vue +0 -68
  50. package/src/components/core/playground-sections/SectionToggles.vue +0 -158
  51. package/src/components/core/playground-sections/SectionTypography.vue +0 -40
  52. package/src/main.js +0 -15
@@ -0,0 +1,112 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { ref } from 'vue'
3
+ import CpTextarea from '@/components/inputs/CpTextarea.vue'
4
+
5
+ const meta = {
6
+ title: 'CpTextarea',
7
+ component: CpTextarea,
8
+ argTypes: {
9
+ modelValue: {
10
+ control: 'text',
11
+ description: 'The textarea value',
12
+ },
13
+ label: {
14
+ control: 'text',
15
+ description: 'Label text for the textarea',
16
+ },
17
+ placeholder: {
18
+ control: 'text',
19
+ description: 'Placeholder text',
20
+ },
21
+ required: {
22
+ control: 'boolean',
23
+ description: 'Whether the field is required',
24
+ },
25
+ disabled: {
26
+ control: 'boolean',
27
+ description: 'Whether the field is disabled',
28
+ },
29
+ isInvalid: {
30
+ control: 'boolean',
31
+ description: 'Whether the field is in an invalid state',
32
+ },
33
+ errorMessage: {
34
+ control: 'text',
35
+ description: 'Error message to display',
36
+ },
37
+ rows: {
38
+ control: 'number',
39
+ description: 'Number of visible rows',
40
+ },
41
+ maxLength: {
42
+ control: 'number',
43
+ description: 'Maximum number of characters allowed',
44
+ },
45
+ },
46
+ } satisfies Meta<typeof CpTextarea>
47
+
48
+ export default meta
49
+ type Story = StoryObj<typeof meta>
50
+
51
+ export const Default: Story = {
52
+ args: {
53
+ label: 'Textarea Label',
54
+ placeholder: 'Enter your message here',
55
+ rows: 4,
56
+ },
57
+ render: (args) => ({
58
+ components: { CpTextarea },
59
+ setup() {
60
+ const value = ref('')
61
+ return { args, value }
62
+ },
63
+ template: `
64
+ <div style="max-width: 400px; padding: 20px;">
65
+ <CpTextarea
66
+ v-model="value"
67
+ v-bind="args"
68
+ />
69
+ </div>
70
+ `,
71
+ }),
72
+ }
73
+
74
+ export const WithError: Story = {
75
+ args: {
76
+ ...Default.args,
77
+ isInvalid: true,
78
+ errorMessage: 'This field is required',
79
+ },
80
+ }
81
+
82
+ export const Required: Story = {
83
+ args: {
84
+ ...Default.args,
85
+ required: true,
86
+ },
87
+ }
88
+
89
+ export const Disabled: Story = {
90
+ args: {
91
+ ...Default.args,
92
+ disabled: true,
93
+ },
94
+ }
95
+
96
+ export const WithMaxLength: Story = {
97
+ args: {
98
+ ...Default.args,
99
+ maxLength: 100,
100
+ label: 'Limited Textarea',
101
+ placeholder: 'Enter up to 100 characters',
102
+ },
103
+ }
104
+
105
+ export const CustomRows: Story = {
106
+ args: {
107
+ ...Default.args,
108
+ rows: 6,
109
+ label: 'Large Textarea',
110
+ placeholder: 'Enter a longer message',
111
+ },
112
+ }
@@ -0,0 +1,147 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import CpToaster from '@/components/feedback-indicators/CpToaster.vue'
3
+
4
+ const meta = {
5
+ title: 'CpToaster',
6
+ component: CpToaster,
7
+ argTypes: {
8
+ title: {
9
+ control: 'text',
10
+ description: 'The title of the toast',
11
+ },
12
+ description: {
13
+ control: 'text',
14
+ description: 'The description of the toast',
15
+ },
16
+ type: {
17
+ control: 'select',
18
+ options: ['info', 'warning', 'success', 'critical'],
19
+ description: 'The type of the toast',
20
+ },
21
+ delayBeforeCloseInMs: {
22
+ control: 'number',
23
+ description: 'Duration in milliseconds before the toast disappears',
24
+ },
25
+ actionLabel: {
26
+ control: 'text',
27
+ description: 'Label for the action button',
28
+ },
29
+ isUnique: {
30
+ control: 'boolean',
31
+ description: 'Whether the toast should be unique',
32
+ },
33
+ },
34
+ } satisfies Meta<typeof CpToaster>
35
+
36
+ export default meta
37
+ type Story = StoryObj<typeof meta>
38
+
39
+ export const Default: Story = {
40
+ args: {
41
+ title: 'Default Toast',
42
+ description: 'This is a default toast message',
43
+ type: 'info',
44
+ delayBeforeCloseInMs: 3000,
45
+ actionLabel: '',
46
+ isUnique: false,
47
+ },
48
+ render: (args) => ({
49
+ template: `
50
+ <div style="padding: 20px;">
51
+ <cp-button @click="addInfoToaster">Show Info Toast</cp-button>
52
+ </div>
53
+ `,
54
+ methods: {
55
+ addInfoToaster() {
56
+ this.$toaster.info({ ...args })
57
+ },
58
+ },
59
+ }),
60
+ }
61
+
62
+ export const DifferentTypes: Story = {
63
+ render: () => ({
64
+ template: `
65
+ <div style="padding: 20px; display: flex; flex-direction: column; gap: 16px;">
66
+ <cp-button @click="addInfoToaster">Show Info Toast</cp-button>
67
+ <cp-button @click="addSuccessToaster">Show Success Toast</cp-button>
68
+ <cp-button @click="addWarningToaster">Show Warning Toast</cp-button>
69
+ <cp-button @click="addCriticalToaster">Show Critical Toast</cp-button>
70
+ </div>
71
+ `,
72
+ methods: {
73
+ addInfoToaster() {
74
+ this.$toaster.info({
75
+ title: 'This is an info toaster',
76
+ description: 'This a description of a toaster',
77
+ isUnique: true,
78
+ })
79
+ },
80
+ addSuccessToaster() {
81
+ this.$toaster.success({
82
+ title: 'This is a success toaster',
83
+ description: 'This a description of a toaster',
84
+ isUnique: true,
85
+ })
86
+ },
87
+ addWarningToaster() {
88
+ this.$toaster.warning({
89
+ title: 'This is a warning toaster',
90
+ description: 'This a description of a toaster',
91
+ isUnique: true,
92
+ })
93
+ },
94
+ addCriticalToaster() {
95
+ this.$toaster.critical({
96
+ title: 'This is a critical toaster',
97
+ description: 'This a description of a toaster',
98
+ isUnique: true,
99
+ })
100
+ },
101
+ },
102
+ }),
103
+ }
104
+
105
+ export const WithAction: Story = {
106
+ render: () => ({
107
+ template: `
108
+ <div style="padding: 20px;">
109
+ <cp-button @click="addLinkToaster">Show Toast with Action</cp-button>
110
+ </div>
111
+ `,
112
+ methods: {
113
+ addLinkToaster() {
114
+ this.$toaster.success({
115
+ title: 'This is a success toaster',
116
+ description: 'Description of a toaster with a link',
117
+ actionLabel: 'See flight information',
118
+ isUnique: true,
119
+ actionMethod: (vm) => {
120
+ vm.closeToaster()
121
+ window.open('http://app.citizenplane.com', '_blank')
122
+ },
123
+ })
124
+ },
125
+ },
126
+ }),
127
+ }
128
+
129
+ export const CustomDuration: Story = {
130
+ render: () => ({
131
+ template: `
132
+ <div style="padding: 20px;">
133
+ <cp-button @click="addLongDurationToaster">Show Long Duration Toast</cp-button>
134
+ </div>
135
+ `,
136
+ methods: {
137
+ addLongDurationToaster() {
138
+ this.$toaster.info({
139
+ title: 'Long Duration Toast',
140
+ description: 'This toast will stay for 10 seconds',
141
+ delayBeforeCloseInMs: 10000,
142
+ isUnique: true,
143
+ })
144
+ },
145
+ },
146
+ }),
147
+ }
@@ -0,0 +1,101 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import CpTooltip from '@/components/atomic-elements/CpTooltip.vue'
3
+
4
+ const meta = {
5
+ title: 'CpTooltip',
6
+ component: CpTooltip,
7
+ argTypes: {
8
+ content: {
9
+ control: 'text',
10
+ description: 'The content to display in the tooltip',
11
+ },
12
+ placement: {
13
+ control: 'select',
14
+ options: ['top', 'right', 'bottom', 'left'],
15
+ description: 'The placement of the tooltip',
16
+ },
17
+ distance: {
18
+ control: 'number',
19
+ description: 'The distance between the tooltip and the target',
20
+ },
21
+ },
22
+ } satisfies Meta<typeof CpTooltip>
23
+
24
+ export default meta
25
+ type Story = StoryObj<typeof meta>
26
+
27
+ export const Default: Story = {
28
+ args: {
29
+ content: 'Tooltip content',
30
+ placement: 'top',
31
+ distance: 8,
32
+ },
33
+ render: (args) => ({
34
+ components: { CpTooltip },
35
+ setup() {
36
+ return { args }
37
+ },
38
+ template: `
39
+ <div style="padding: 100px; text-align: center;">
40
+ <CpTooltip v-bind="args">
41
+ <button>Hover me</button>
42
+ </CpTooltip>
43
+ </div>
44
+ `,
45
+ }),
46
+ }
47
+
48
+ export const AllPlacements: Story = {
49
+ render: (args) => ({
50
+ components: { CpTooltip },
51
+ setup() {
52
+ return { args }
53
+ },
54
+ template: `
55
+ <div style="display: flex; flex-direction: column; gap: 20px; padding: 100px;">
56
+ <div>
57
+ <CpTooltip content="Top tooltip" placement="top">
58
+ <button>Top</button>
59
+ </CpTooltip>
60
+ </div>
61
+ <div>
62
+ <CpTooltip content="Right tooltip" placement="right">
63
+ <button>Right</button>
64
+ </CpTooltip>
65
+ </div>
66
+ <div>
67
+ <CpTooltip content="Bottom tooltip" placement="bottom">
68
+ <button>Bottom</button>
69
+ </CpTooltip>
70
+ </div>
71
+ <div>
72
+ <CpTooltip content="Left tooltip" placement="left">
73
+ <button>Left</button>
74
+ </CpTooltip>
75
+ </div>
76
+ </div>
77
+ `,
78
+ }),
79
+ }
80
+
81
+ export const WithHTMLContent: Story = {
82
+ args: {
83
+ placement: 'top',
84
+ },
85
+ render: (args) => ({
86
+ components: { CpTooltip },
87
+ setup() {
88
+ return { args }
89
+ },
90
+ template: `
91
+ <div style="padding: 100px; text-align: center;">
92
+ <CpTooltip v-bind="args">
93
+ <button type="button">Trigger HTML content</button>
94
+ <template #content>
95
+ <p>HTML <b>Content</b> and <u>underlined</u></p>
96
+ </template>
97
+ </CpTooltip>
98
+ </div>
99
+ `,
100
+ }),
101
+ }
@@ -0,0 +1,85 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { ref } from 'vue'
3
+ import TransitionExpand from '@/components/helpers-utilities/TransitionExpand.vue'
4
+
5
+ const meta = {
6
+ title: 'TransitionExpand',
7
+ component: TransitionExpand,
8
+ argTypes: {
9
+ // No props to document as this is a utility component
10
+ },
11
+ } satisfies Meta<typeof TransitionExpand>
12
+
13
+ export default meta
14
+ type Story = StoryObj<typeof meta>
15
+
16
+ const wrapperStyle = 'display: flex; flex-direction: column; align-items: center; gap: 12px;'
17
+
18
+ export const Default: Story = {
19
+ render: () => ({
20
+ components: { TransitionExpand },
21
+ setup() {
22
+ const isExpanded = ref(false)
23
+ return { isExpanded }
24
+ },
25
+ template: `
26
+ <div style="${wrapperStyle}">
27
+ <CpButton
28
+ @click="isExpanded = !isExpanded"
29
+ appearance="primary"
30
+ color="purple"
31
+ >
32
+ {{ isExpanded ? 'Collapse' : 'Expand' }}
33
+ </CpButton>
34
+
35
+ <TransitionExpand>
36
+ <div v-if="isExpanded" style="
37
+ background: #F3F4F6;
38
+ border-radius: 6px;
39
+ ">
40
+ <h3 style="margin: 0 0 8px 0;">Expanded Content</h3>
41
+ <p style="margin: 0;">
42
+ This content will smoothly expand and collapse with a nice animation.
43
+ The height transition is handled automatically by the TransitionExpand component.
44
+ </p>
45
+ </div>
46
+ </TransitionExpand>
47
+ </div>
48
+ `,
49
+ }),
50
+ }
51
+
52
+ export const WithLongContent: Story = {
53
+ render: () => ({
54
+ components: { TransitionExpand },
55
+ setup() {
56
+ const isExpanded = ref(false)
57
+ return { isExpanded }
58
+ },
59
+ template: `
60
+ <div style="${wrapperStyle}">
61
+ <CpButton
62
+ @click="isExpanded = !isExpanded"
63
+ appearance="primary"
64
+ color="purple"
65
+ >
66
+ {{ isExpanded ? 'Collapse' : 'Expand' }}
67
+ </CpButton>
68
+
69
+ <TransitionExpand>
70
+ <div v-if="isExpanded" style="
71
+ background: #F3F4F6;
72
+ border-radius: 6px;
73
+ ">
74
+ <h3 style="margin: 0 0 8px 0;">Long Content Example</h3>
75
+ <p v-for="i in 5" :key="i" style="margin: 0 0 8px 0;">
76
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
77
+ incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
78
+ nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
79
+ </p>
80
+ </div>
81
+ </TransitionExpand>
82
+ </div>
83
+ `,
84
+ }),
85
+ }
@@ -0,0 +1,31 @@
1
+ import path from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ import { defineWorkspace } from 'vitest/config'
5
+
6
+ import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'
7
+
8
+ const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url))
9
+
10
+ // More info at: https://storybook.js.org/docs/writing-tests/test-addon
11
+ export default defineWorkspace([
12
+ 'vite.config.cjs',
13
+ {
14
+ extends: 'vite.config.cjs',
15
+ plugins: [
16
+ // The plugin will run tests for the stories defined in your Storybook config
17
+ // See options at: https://storybook.js.org/docs/writing-tests/test-addon#storybooktest
18
+ storybookTest({ configDir: path.join(dirname, '.storybook') }),
19
+ ],
20
+ test: {
21
+ name: 'storybook',
22
+ browser: {
23
+ enabled: true,
24
+ headless: true,
25
+ name: 'chromium',
26
+ provider: 'playwright',
27
+ },
28
+ setupFiles: ['.storybook/vitest.setup.js'],
29
+ },
30
+ },
31
+ ])
package/src/App.vue DELETED
@@ -1,110 +0,0 @@
1
- <template>
2
- <div class="designPlayground">
3
- <header class="designPlayground__header header">
4
- <pimp-logo class="header__logo" />
5
- <cp-heading :size="700" heading-level="h2" class="header__title">Pimp</cp-heading>
6
- <div class="header__tagline">v {{ version }}</div>
7
- </header>
8
- <div class="designPlayground__container">
9
- <section-lists-and-tables />
10
- <section-atomic-elements />
11
- <section-typography />
12
- <section-buttons />
13
- <section-inputs />
14
- <section-selects />
15
- <section-select-menus />
16
- <section-date-pickers />
17
- <section-toasters />
18
- <section-toggles />
19
- <section-feedback-indicators />
20
- <section-dialog />
21
- </div>
22
- </div>
23
- </template>
24
-
25
- <script>
26
- import pkg from '../package.json'
27
-
28
- import PimpLogo from './components/logo/Pimp.vue'
29
- import SectionAtomicElements from './components/core/playground-sections/SectionAtomicElements.vue'
30
- import SectionTypography from './components/core/playground-sections/SectionTypography.vue'
31
- import SectionButtons from './components/core/playground-sections/SectionButtons.vue'
32
- import SectionInputs from './components/core/playground-sections/SectionInputs.vue'
33
- import SectionSelects from './components/core/playground-sections/SectionSelects.vue'
34
- import SectionSelectMenus from './components/core/playground-sections/SectionSelectMenus.vue'
35
- import SectionDatePickers from './components/core/playground-sections/SectionDatePickers.vue'
36
- import SectionToasters from './components/core/playground-sections/SectionToasters.vue'
37
- import SectionToggles from './components/core/playground-sections/SectionToggles.vue'
38
- import SectionFeedbackIndicators from './components/core/playground-sections/SectionFeedbackIndicators.vue'
39
- import SectionListsAndTables from './components/core/playground-sections/SectionListsAndTables.vue'
40
- import SectionDialog from './components/core/playground-sections/SectionDialog.vue'
41
-
42
- export default {
43
- components: {
44
- PimpLogo,
45
- SectionAtomicElements,
46
- SectionTypography,
47
- SectionButtons,
48
- SectionInputs,
49
- SectionSelects,
50
- SectionSelectMenus,
51
- SectionDatePickers,
52
- SectionToasters,
53
- SectionToggles,
54
- SectionFeedbackIndicators,
55
- SectionListsAndTables,
56
- SectionDialog,
57
- },
58
- computed: {
59
- version() {
60
- return pkg.version
61
- },
62
- },
63
- }
64
- </script>
65
-
66
- <style lang="scss">
67
- .designPlayground {
68
- position: relative;
69
-
70
- &__header {
71
- position: sticky;
72
- top: 0;
73
- left: 0;
74
- z-index: 2;
75
- border-bottom: 1px solid colors.$neutral-dark-4;
76
- background-color: colors.$neutral-light;
77
- display: flex;
78
- align-items: center;
79
- padding: fn.px-to-rem(16);
80
- }
81
-
82
- .header {
83
- &__logo {
84
- width: fn.px-to-rem(32);
85
- height: auto;
86
- }
87
-
88
- &__title {
89
- margin: 0 fn.px-to-rem(8);
90
- }
91
- }
92
-
93
- &__container {
94
- margin: 0 auto;
95
- padding: fn.px-to-rem(16);
96
- width: 100%;
97
- max-width: fn.px-to-rem(900);
98
- }
99
-
100
- section {
101
- width: 100%;
102
- padding: fn.px-to-rem(20);
103
- border-top: fn.px-to-rem(1) solid colors.$neutral-dark-4;
104
-
105
- &:first-child {
106
- border-top: none;
107
- }
108
- }
109
- }
110
- </style>
@@ -1,83 +0,0 @@
1
- <template>
2
- <section-container section-title="Atomic elements" class="sectionAtomicElements">
3
- <div class="item">
4
- <h3>Badge</h3>
5
- <div class="sectionAtomicElements__badges">
6
- <cp-badge
7
- v-for="{ color, label, icon, plain, solid, clear } in badgeList"
8
- :key="color"
9
- :color="color"
10
- :icon="icon"
11
- :is-plain="plain"
12
- :is-solid="solid"
13
- :is-clearable="clear"
14
- class="sectionAtomicElements__badge"
15
- >
16
- {{ label }}
17
- </cp-badge>
18
- </div>
19
- </div>
20
- </section-container>
21
- </template>
22
-
23
- <script>
24
- import SectionContainer from '@/components/core/playground-sections/SectionContainer.vue'
25
-
26
- export default {
27
- components: {
28
- SectionContainer,
29
- },
30
- data() {
31
- return {
32
- badgeList: [
33
- {
34
- color: '',
35
- label: 'Default plain clearable',
36
- clear: true,
37
- },
38
- {
39
- color: '',
40
- label: 'Default plain w/ icon',
41
- icon: 'copy',
42
- },
43
- {
44
- color: 'neutral',
45
- label: 'Beta',
46
- },
47
- {
48
- color: 'green',
49
- label: 'Solid green clearable',
50
- solid: true,
51
- clear: true,
52
- },
53
- {
54
- color: 'red',
55
- label: 'Default red',
56
- icon: 'copy',
57
- },
58
- {
59
- color: 'blue',
60
- label: 'Default blue clearable',
61
- clear: true,
62
- },
63
- {
64
- color: 'purple',
65
- label: 'Solid purple w/ icon',
66
- icon: 'copy',
67
- solid: true,
68
- },
69
- ],
70
- }
71
- },
72
- }
73
- </script>
74
-
75
- <style lang="scss">
76
- .sectionAtomicElements {
77
- &__badges {
78
- display: flex;
79
- flex-wrap: wrap;
80
- gap: sp.$space;
81
- }
82
- }
83
- </style>