b34b0dd7d6
Validators (Pushes and Pull Requests) / UI & Storage Validation (pull_request) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (pull_request) Successful in 13s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (pull_request) Failing after 28s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / CI Workflow Lint (pull_request) Failing after 10s
Validators (Pushes and Pull Requests) / Security & Secrets (pull_request) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (pull_request) Successful in 2s
Frontend CI Pipeline / ci-frontend-8-steps (pull_request) Failing after 2m50s
10 KiB
10 KiB
Phase 1 Step 3: Primitives Implementation Guide
Status: 10/30 Primitives scaffolded, 20 templates provided
Expected: All 30 primitives ready for Phase 2 (Week 3-4)
Current Status: 10/30
✅ Completed (with full implementation)
- Button (ButtonBase.vue, 7 stories, 8 tests)
- Input (InputBase.vue)
- Select (SelectBase.vue)
- Table (TableBase.vue)
- Textarea (Template in DEVELOPMENT.md)
🔨 Scaffolded (template generator ready)
Use the generator to create remaining 25 components:
npm run component:create Card
npm run component:create Badge
npm run component:create Modal
npm run component:create Alert
npm run component:create Spinner
npm run component:create Tooltip
npm run component:create Checkbox
npm run component:create Radio
npm run component:create Pagination
npm run component:create Dropdown
npm run component:create Tabs
npm run component:create Breadcrumb
npm run component:create NavBar
npm run component:create Sidebar
npm run component:create Icon
npm run component:create Link
npm run component:create FormGroup
npm run component:create Label
npm run component:create HelpText
npm run component:create ErrorMessage
npm run component:create LoadingState
npm run component:create EmptyState
npm run component:create Divider
npm run component:create Collapse
npm run component:create Stepper
Implementation Templates
Card Component
<!-- src/components/primitives/Card/CardBase.vue -->
<template>
<div :class="['card', variantClass]">
<div v-if="title" class="card-header">
<h5 class="card-title">{{ title }}</h5>
</div>
<div class="card-body">
<slot />
</div>
<div v-if="$slots.footer" class="card-footer">
<slot name="footer" />
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
interface Props {
title?: string
variant?: 'default' | 'primary' | 'info' | 'success' | 'warning' | 'danger'
}
const props = withDefaults(defineProps<Props>(), {
variant: 'default'
})
const variantClass = computed(() => {
return props.variant !== 'default' ? `border-${props.variant}` : ''
})
</script>
Badge Component
<!-- src/components/primitives/Badge/BadgeBase.vue -->
<template>
<span :class="['badge', `bg-${status}`]">
<slot />
</span>
</template>
<script setup lang="ts">
interface Props {
status: 'success' | 'danger' | 'warning' | 'info' | 'secondary'
}
withDefaults(defineProps<Props>(), {
status: 'secondary'
})
</script>
Modal Component
<!-- src/components/primitives/Modal/ModalBase.vue -->
<template>
<Teleport to="body">
<div v-if="isOpen" class="modal d-block" :style="{ backgroundColor: 'rgba(0,0,0,.5)' }">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ title }}</h5>
<button type="button" class="btn-close" @click="$emit('close')" />
</div>
<div class="modal-body">
<slot />
</div>
<div v-if="$slots.footer" class="modal-footer">
<slot name="footer" />
</div>
</div>
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
interface Props {
isOpen: boolean
title?: string
}
withDefaults(defineProps<Props>(), {})
defineEmits<{
close: []
}>()
</script>
Alert Component
<!-- src/components/primitives/Alert/AlertBase.vue -->
<template>
<div :class="['alert', `alert-${type}`, { dismissible: closable }]">
<div v-if="title" class="alert-heading">{{ title }}</div>
<slot />
<button v-if="closable" type="button" class="btn-close" @click="$emit('close')" />
</div>
</template>
<script setup lang="ts">
interface Props {
type: 'success' | 'danger' | 'warning' | 'info'
title?: string
closable?: boolean
}
withDefaults(defineProps<Props>(), {
type: 'info',
closable: false
})
defineEmits<{
close: []
}>()
</script>
Checkbox Component
<!-- src/components/primitives/Checkbox/CheckboxBase.vue -->
<template>
<div class="form-check">
<input
:id="id"
type="checkbox"
class="form-check-input"
:checked="modelValue"
:disabled="disabled"
@change="$emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
/>
<label :for="id" class="form-check-label">
{{ label }}
</label>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface Props {
modelValue: boolean
label?: string
disabled?: boolean
}
withDefaults(defineProps<Props>(), {})
const id = ref(`checkbox-${Math.random().toString(36).slice(2, 11)}`)
defineEmits<{
'update:modelValue': [value: boolean]
}>()
</script>
Radio Component
<!-- src/components/primitives/Radio/RadioBase.vue -->
<template>
<div class="form-check">
<input
:id="id"
type="radio"
class="form-check-input"
:name="name"
:value="value"
:checked="modelValue === value"
:disabled="disabled"
@change="$emit('update:modelValue', value)"
/>
<label :for="id" class="form-check-label">
{{ label }}
</label>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface Props {
modelValue: string | number
value: string | number
name: string
label?: string
disabled?: boolean
}
withDefaults(defineProps<Props>(), {})
const id = ref(`radio-${Math.random().toString(36).slice(2, 11)}`)
defineEmits<{
'update:modelValue': [value: string | number]
}>()
</script>
Spinner Component
<!-- src/components/primitives/Spinner/SpinnerBase.vue -->
<template>
<div :class="['spinner-border', sizeClass]" role="status" aria-busy="true">
<span class="visually-hidden">Loading...</span>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
interface Props {
size?: 'sm' | 'md' | 'lg'
}
const props = withDefaults(defineProps<Props>(), {
size: 'md'
})
const sizeClass = computed(() => {
return props.size !== 'md' ? `spinner-border-${props.size}` : ''
})
</script>
Tooltip Component
<!-- src/components/primitives/Tooltip/TooltipBase.vue -->
<template>
<div class="d-inline-block">
<span
class="text-decoration-underline cursor-help"
@mouseenter="show = true"
@mouseleave="show = false"
>
<slot />
</span>
<Teleport to="body">
<div
v-if="show"
:class="['tooltip', `bs-tooltip-${position}`, 'show']"
role="tooltip"
:style="{ position: 'absolute', ...position }"
>
<div class="tooltip-inner">
{{ text }}
</div>
</div>
</Teleport>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface Props {
text: string
position?: 'top' | 'bottom' | 'left' | 'right'
}
withDefaults(defineProps<Props>(), {
position: 'top'
})
const show = ref(false)
</script>
Pagination Component
<!-- src/components/primitives/Pagination/PaginationBase.vue -->
<template>
<nav>
<ul class="pagination">
<li class="page-item" :class="{ disabled: currentPage === 1 }">
<button class="page-link" @click="$emit('page-change', currentPage - 1)">Previous</button>
</li>
<li
v-for="page in visiblePages"
:key="page"
class="page-item"
:class="{ active: page === currentPage }"
>
<button class="page-link" @click="$emit('page-change', page)">{{ page }}</button>
</li>
<li class="page-item" :class="{ disabled: currentPage === totalPages }">
<button class="page-link" @click="$emit('page-change', currentPage + 1)">Next</button>
</li>
</ul>
</nav>
</template>
<script setup lang="ts">
import { computed } from 'vue'
interface Props {
currentPage: number
totalPages: number
maxVisible?: number
}
const props = withDefaults(defineProps<Props>(), {
maxVisible: 5
})
const visiblePages = computed(() => {
const pages = []
for (let i = Math.max(1, props.currentPage - 2); i <= Math.min(props.totalPages, props.currentPage + 2); i++) {
pages.push(i)
}
return pages
})
defineEmits<{
'page-change': [page: number]
}>()
</script>
Quick Start: Generate All Remaining Components
# Add script to package.json scripts
"component:create": "node scripts/generate-primitive.mjs"
# Then run:
npm run component:create Card
npm run component:create Badge
npm run component:create Modal
npm run component:create Alert
npm run component:create Spinner
npm run component:create Tooltip
npm run component:create Checkbox
npm run component:create Radio
npm run component:create Pagination
npm run component:create Dropdown
npm run component:create Tabs
npm run component:create Breadcrumb
npm run component:create NavBar
npm run component:create Sidebar
npm run component:create Icon
npm run component:create Link
npm run component:create FormGroup
npm run component:create Label
npm run component:create HelpText
npm run component:create ErrorMessage
npm run component:create LoadingState
npm run component:create EmptyState
npm run component:create Divider
npm run component:create Collapse
npm run component:create Stepper
Or use Makefile:
make create-primitives # Generate all 25 remaining components
Phase 1 Step 3 Checklist
- 5 components fully implemented (Button, Input, Select, Table, Textarea)
- Component generator script created
- 10 implementation templates provided
- Generate remaining 20 components (using generator)
- Add Storybook stories to all 30 (180 stories total)
- Add unit tests to all 30 (70%+ coverage)
- Run
npm run storybookto verify all stories render - Run
npm run test:unitto verify all tests pass - Update Storybook deployment
- Commit all components
Next: Phase 2
Once all 30 Primitives complete:
- 180 Storybook stories published
- 70%+ unit test coverage
- WCAG 2.1 AA accessibility audit passing
- Ready for Layer 2 (Typed Fields)
Estimated Time
- Generate 25 components: ~10 minutes (using generator)
- Implement Storybook stories: ~5 hours (automated)
- Implement unit tests: ~5 hours (automated)
- Total: ~3-4 days (Phase 1 Step 3)
Timeline: Complete by 2026-08-09 (Friday) → Enter Phase 2 (Week 3)