Skip to content

Commit 578cf76

Browse files
committed
Initial add of search box to title bar
1 parent 8a46b6e commit 578cf76

12 files changed

Lines changed: 431 additions & 107 deletions

File tree

_TODO.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,31 @@ if (window.matchMedia) {
9090
}
9191
```
9292

93+
## Home Page Ordering
94+
95+
- Hero
96+
- About Preview ("Building the Future of Software Development")
97+
- Featured Services
98+
- (Todo) Turn-Key Backstage IDP Implementation
99+
- Testimonials
100+
- Latest Articles
101+
- Primary CTA ("Ready to Transform Your Development Process?") with links to /contact and /services
102+
- Newsletter Signup
103+
- Skills/Technologies Preview
104+
105+
Maybe change the "ready..." in the hero to "get in touch..."
106+
107+
## Search Box
108+
109+
1. Need to make sure we have an action to update the search index on content changes
110+
2. Need to get the "x" back to clear search terms
111+
3. Maybe need a 1px border
112+
4. Hide the nav link items on desktop and the company name is hidden on mobile when the search box is out
113+
5. Make sure state is being kept in nanostores
114+
6. What happens when search query exceeds size of search box? Drop down to second line?
115+
7. Need to improve quality of search results in drop down box - maybe some description text for each item
116+
8. Style drop down box of search results from search box
117+
93118
## Image generation models
94119

95120
- dall-e (OpenAI)
@@ -110,10 +135,6 @@ High Resolution: Seedream 4.0 is capable of generating native high-resolution im
110135

111136
Complex Text and Instruction Following: The model demonstrates an enhanced understanding of complex semantic prompts and excels in rendering dense and accurate text within the generated images, a common weakness in other AI image models.
112137

113-
## Move Tags Page
114-
115-
The /tags route is really just the /articles route but better. We should redirect from /tags to /articles, and fix the breadcrumb so that it goes to /articles if someone clicks on the /tags part.
116-
117138
## Content Instructions
118139

119140
We have generated detailed outlines for each of the MDX proposed articles we have in src/content/articles. It's time to write our articles. Review ./CONTENT.md for the goals and voice to use in our website. Each outline includes various components we use on our site that can be incorporated into the final article but do not have to be included if they don't fit into the content well or are excessive.

src/components/Animations/Hero/index.astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ const { pretitle, benefits } = Astro.props as Props
6565
<Button href="/about" variant="success" text="Read My Story" />
6666
<a
6767
href="/contact"
68-
class="py-3 text-primary font-bold underline underline-offset-4 decoration-2 decoration-dotted decoration-primary transition-colors hover:text-content-offset hover:decoration-content-offset focus-visible:text-content-offset focus-visible:underline focus-visible:decoration-2 focus-visible:decoration-dotted focus-visible:decoration-content-offset"
68+
class="sm:ml-6 py-3 text-primary font-bold underline underline-offset-4 decoration-2 decoration-dotted decoration-primary transition-colors hover:text-content-offset hover:decoration-content-offset focus-visible:text-content-offset focus-visible:underline focus-visible:decoration-2 focus-visible:decoration-dotted focus-visible:decoration-content-offset"
6969
>
70-
Get in Touch
70+
Get in Touch <span aria-hidden="true" class="md:text-3xl">→</span>
7171
</a>
7272
</div>
7373
</div>

src/components/Header/__tests__/index.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ describe('Header (Astro)', () => {
5050
expect(themeSvg?.getAttribute('aria-hidden')).toBe('true')
5151
expect(themeSvg?.getAttribute('focusable')).toBe('false')
5252
expect(themeSvg?.querySelector('title')).toBeNull()
53+
54+
const searchToggleButton = window.document.querySelector('button[data-search-toggle]')
55+
expect(searchToggleButton).toBeTruthy()
56+
expect(searchToggleButton?.getAttribute('aria-label')).toBe('Search')
57+
expect(searchToggleButton?.getAttribute('aria-expanded')).toBe('false')
5358
})
5459
})
5560
})

src/components/Header/index.astro

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
import Brand from '@components/Brand/index.astro'
33
import Navigation from '@components/Navigation/index.astro'
4+
import SearchBar from '@components/Search/SearchBar/index.astro'
45
import ThemeButton from '@components/ThemePicker/ThemeButton.astro'
56
67
export interface Props {
@@ -17,17 +18,26 @@ const { path } = Astro.props
1718
--header-icon-size: calc(3.5em * 0.75);
1819
--header-icon-svg-size: calc(var(--header-icon-size) * 0.7);
1920
--circle-stroke: calc(var(--header-icon-size) * 2 * 3.1416);
21+
--header-search-panel-width: 18rem;
22+
--header-search-panel-overhang: calc(var(--header-icon-size) + 0.5rem);
23+
--header-search-panel-width-open: calc(var(--header-search-panel-width) + var(--header-search-panel-overhang));
24+
--header-search-panel-right: calc(-1 * var(--header-search-panel-overhang));
2025
}
2126

2227
@media (width >= 640px) {
2328
:root {
2429
--header-icon-size: calc(3.5em * 0.85);
30+
--header-search-panel-width: 20rem;
2531
}
2632
}
2733

2834
@media (width >= 1024px) {
2935
:root {
3036
--header-icon-size: calc(3.5em);
37+
--header-search-panel-width: 24rem;
38+
--header-search-panel-overhang: 0px;
39+
--header-search-panel-width-open: var(--header-search-panel-width);
40+
--header-search-panel-right: 0px;
3141
}
3242
}
3343

@@ -79,8 +89,16 @@ const { path } = Astro.props
7989
{/* Theme picker toggle button to show modal */}
8090
<span
8191
id="header__theme-icon"
82-
class="order-3 lg:order-4 ml-2 mr-2 lg:mr-0 shrink-0 w-(--header-icon-size) relative z-(--z-content-overlay)"
92+
class="order-3 lg:order-4 ml-2 mr-2 block shrink-0 w-(--header-icon-size) h-(--header-icon-size) relative z-(--z-content-overlay)"
8393
>
8494
<ThemeButton />
8595
</span>
96+
97+
{/* Header search icon that expands left into a search panel */}
98+
<span
99+
id="header__search-icon"
100+
class="order-4 lg:order-5 mr-2 lg:mr-0 block shrink-0 w-(--header-icon-size) h-(--header-icon-size) relative z-(--z-content-overlay) lg:z-(--z-nav)"
101+
>
102+
<SearchBar variant="header" />
103+
</span>
86104
</header>

src/components/Navigation/NavToggle.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import styles from './navToggle.module.css'
1010
data-testid="nav-toggle"
1111
class:list={[
1212
'nav-toggle-btn',
13-
'lg:hidden inline-flex items-center justify-center rounded-full border-0 bg-primary text-content-inverse transition-colors duration-150 ease-linear hover:bg-spotlight focus:bg-primary-offset focus:outline-none',
13+
'lg:hidden inline-flex items-center justify-center w-(--header-icon-size) h-(--header-icon-size) leading-none rounded-full border-0 bg-primary text-content-inverse transition-colors duration-150 ease-linear hover:bg-spotlight focus:bg-primary-offset focus:outline-none',
1414
styles.button,
1515
]}
1616
type="button"

src/components/Navigation/index.astro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ const { path } = Astro.props
1717
<Menu path={path} />
1818
</span>
1919
{/* Mobile navigation toggle button to show menu on full-page splash screen */}
20-
<span id="header__nav-icon" class="order-4 block shrink-0 relative z-(--z-mobile-close-btn) lg:hidden">
20+
<span
21+
id="header__nav-icon"
22+
class="order-5 block shrink-0 w-(--header-icon-size) h-(--header-icon-size) relative z-(--z-mobile-close-btn) lg:hidden"
23+
>
2124
<NavToggle />
2225
</span>
2326
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
import SearchBar from '@components/Search/SearchBar/index.astro'
3+
4+
const { placeholder = 'Search…', initialQuery = '' } = Astro.props
5+
---
6+
7+
<SearchBar variant="header" placeholder={placeholder} initialQuery={initialQuery} />

src/components/Search/SearchBar/client/__tests__/index.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest'
22
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
33
import SearchBarFixture from '@components/Search/SearchBar/client/__fixtures__/index.fixture.astro'
4+
import SearchBarHeaderFixture from '@components/Search/SearchBar/client/__fixtures__/header.fixture.astro'
45
import type { SearchBarElement as SearchBarElementInstance } from '../index'
56
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
67
import { executeRender } from '@test/unit/helpers/litRuntime'
@@ -60,6 +61,28 @@ describe('SearchBar web component', () => {
6061
})
6162
}
6263

64+
const runHeaderComponentRender = async (
65+
assertion: (_context: {
66+
element: SearchBarElementInstance
67+
window: Window & typeof globalThis
68+
}) => Promise<void> | void
69+
): Promise<void> => {
70+
await executeRender<SearchBarModule>({
71+
container,
72+
component: SearchBarHeaderFixture,
73+
moduleSpecifier: '@components/Search/SearchBar/client/index',
74+
waitForReady: async (element: SearchBarElementInstance) => {
75+
await element.updateComplete
76+
},
77+
assert: async ({ element, window }) => {
78+
if (!window) {
79+
throw new Error('Missing JSDOM window for SearchBar test.')
80+
}
81+
await assertion({ element, window })
82+
},
83+
})
84+
}
85+
6386
it('hides results when query is too short', async () => {
6487
await runComponentRender(async ({ element, window }) => {
6588
const input = element.querySelector('[data-search-input]') as HTMLInputElement
@@ -108,4 +131,26 @@ describe('SearchBar web component', () => {
108131

109132
vi.useRealTimers()
110133
})
134+
135+
it('toggles open and closes on Escape in header variant', async () => {
136+
await runHeaderComponentRender(async ({ element, window }) => {
137+
const toggleBtn = element.querySelector('[data-search-toggle]') as HTMLButtonElement
138+
const input = element.querySelector('[data-search-input]') as HTMLInputElement
139+
140+
expect(toggleBtn.getAttribute('aria-expanded')).toBe('false')
141+
expect(input.classList.contains('hidden')).toBe(true)
142+
143+
toggleBtn.click()
144+
await flushMicrotasks()
145+
146+
expect(toggleBtn.getAttribute('aria-expanded')).toBe('true')
147+
expect(input.classList.contains('hidden')).toBe(false)
148+
149+
input.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
150+
await flushMicrotasks()
151+
152+
expect(toggleBtn.getAttribute('aria-expanded')).toBe('false')
153+
expect(input.classList.contains('hidden')).toBe(true)
154+
})
155+
})
111156
})

0 commit comments

Comments
 (0)