Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Create the InputField and SearchBar components #380

Merged
merged 38 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3ae1f2a
Add all dark-charcoal colors from styleguide
sarayourfriend Oct 27, 2021
c0d9876
Add base button component
sarayourfriend Oct 27, 2021
4ca2feb
Fix event handling
sarayourfriend Oct 27, 2021
a34763d
Fix typo
sarayourfriend Oct 28, 2021
915c02c
Add large size and fix tertiary-active style
sarayourfriend Oct 28, 2021
72c9500
Add one more variant to cover menu buttons and rename active to pressed
sarayourfriend Oct 28, 2021
aa36b88
Add a11y enhancements to the VButton component
sarayourfriend Oct 29, 2021
5571c16
Correctly handle button input types
sarayourfriend Oct 29, 2021
4b2494a
Run code to set direction on mount
dhruvkb Oct 28, 2021
66e4f50
Add magnifier icon for search
dhruvkb Oct 28, 2021
8c655d6
Set a zero width for ring to hide it
dhruvkb Oct 28, 2021
eb90e67
Create an `InputField` component
dhruvkb Oct 28, 2021
dbf59de
Add start and end margins to the input and extra text
dhruvkb Nov 3, 2021
734a73f
Update the component to match the new designs with individual focus o…
dhruvkb Nov 3, 2021
b7fef48
Allow changing type with a fallback to 'text'
dhruvkb Nov 3, 2021
b65f224
Create the presentational `SearchBar` component
dhruvkb Nov 3, 2021
b768fc3
Change the form's behaviour to use submission as the event trigger
dhruvkb Nov 4, 2021
ea8fe74
Pass attributes down to the `InputField`
dhruvkb Nov 4, 2021
9175908
Add specs for the input field
dhruvkb Nov 4, 2021
cce3148
Add SR text and ARIA label
dhruvkb Nov 4, 2021
4582019
Add specs for the `SearchBar` component
dhruvkb Nov 4, 2021
34a0053
Change extra text to a `slot` to enable advanced markup
dhruvkb Nov 4, 2021
e081937
Merge branch 'main' into input_field
dhruvkb Nov 4, 2021
f06e44e
Replace `onMounted` with `immediate` flag
dhruvkb Nov 9, 2021
a7d1e98
Selectively remove cross from search input field
dhruvkb Nov 9, 2021
b34a843
Make the input field labelled by default, with option to attach more
dhruvkb Nov 10, 2021
c7f8032
Set the placeholder color to match Figma specification
dhruvkb Nov 10, 2021
ef73c50
Use `group-hover` to style search field and button together on hover
dhruvkb Nov 10, 2021
0d9cbce
Merge branch 'main' of https://github.com/WordPress/openverse-fronten…
dhruvkb Nov 10, 2021
894d43d
Merge branch 'main' of https://github.com/WordPress/openverse-fronten…
dhruvkb Nov 11, 2021
be2b67f
Move `watch` inside `onMounted`
dhruvkb Nov 11, 2021
616400a
Make the focus style identical to hover
dhruvkb Nov 12, 2021
cc1ab67
Replace `hover` & `focus` combo with `active` state
dhruvkb Nov 12, 2021
447a973
Change a11y requirement to allow at least one of nesting and ID
dhruvkb Nov 17, 2021
61687b6
Make `fieldId` and `labelText` required props
dhruvkb Nov 17, 2021
b8e9084
Fix bug in field width
dhruvkb Nov 17, 2021
76a97e1
Propagate listeners to nested `<input>`
dhruvkb Nov 17, 2021
5e6dc7d
Update specs to match component changes
dhruvkb Nov 17, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions .storybook/decorators/with-rtl.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vue from 'vue'

import { ref, watch, useContext } from '@nuxtjs/composition-api'
import { ref, watch, useContext, onMounted } from '@nuxtjs/composition-api'
import { useEffect } from '@storybook/client-api'

const languageDirection = Vue.observable({ value: 'ltr' })
Expand All @@ -16,15 +16,19 @@ export const WithRTL = (story, context) => {
setup() {
const element = ref()
const { i18n } = useContext()
const setLanguage = (direction) => {
element.value.ownerDocument.documentElement.setAttribute(
'dir',
direction?.value ?? 'ltr'
)
}
dhruvkb marked this conversation as resolved.
Show resolved Hide resolved
watch(languageDirection, (direction) => {
i18n.localeProperties.dir = direction.value
if (element.value) {
element.value.ownerDocument.documentElement.setAttribute(
'dir',
direction.value
)
setLanguage(direction)
}
})
onMounted(setLanguage)
return { element }
},
}
Expand Down
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
module.exports = {
globals: {
'vue-jest': {
experimentalCSSCompile: false,
},
},
moduleFileExtensions: ['js', 'vue', 'json'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
Expand Down
32 changes: 18 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"nuxt": "^2.15.4",
"nuxt-ssr-cache": "^1.5.2",
"patch-package": "^6.4.7",
"reakit-utils": "^0.15.2",
"sass": "^1.34.0",
"sass-loader": "^10.1.1",
"uuid": "^8.3.2",
Expand Down
6 changes: 6 additions & 0 deletions src/assets/icons/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 85 additions & 0 deletions src/components/Header/SearchBar/SearchBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<template>
<form class="search-bar group flex flex-row">
<InputField
v-model="text"
class="flex-grow input-field"
:connection-sides="['end']"
input-id="search-bar"
type="search"
name="q"
>
{{ extraText }}
</InputField>
<SearchButton @click="handleSearch" />
</form>
</template>

<script>
import { computed } from '@nuxtjs/composition-api'

import InputField from '~/components/InputField/InputField.vue'
import SearchButton from '~/components/Header/SearchBar/SearchButton.vue'

/**
* Displays a text field for a search query and is attached to an action button
* that fires a search request. The loading state and number of hits are also
* displayed in the bar itself.
*/
export default {
name: 'SearchBar',
components: {
InputField,
SearchButton,
},
model: {
prop: 'value',
event: 'input',
},
props: {
/**
* the search query given as input to the field
*/
value: {
type: String,
default: '',
},
/**
* the extra text to display inside the text field; This can be a status
* message like 'Loading' or info such as the number of search results.
*/
extraText: {
type: String,
},
},
setup(props, { emit }) {
const text = computed({
get() {
return props.value
},
set(value) {
emit('input', value)
},
})

const handleSearch = () => {
emit('search')
}

return {
text,

handleSearch,
}
},
}
</script>

<style>
/* Removes the cross icon to clear the field */
obulat marked this conversation as resolved.
Show resolved Hide resolved
input[type='search']::-webkit-search-decoration,
input[type='search']::-webkit-search-cancel-button,
input[type='search']::-webkit-search-results-button,
input[type='search']::-webkit-search-results-decoration {
-webkit-appearance: none;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this? It's especially useful on mobile.

cc @panchovm

Also the way these styles are applied means they will be applied globally. We should use either scoped or module and associate these modifications to a specific class at the very least.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't be scoped because the input-field being styled is in a different component. But made the selector more "selective" in a7d1e98.

Copy link
Contributor

@sarayourfriend sarayourfriend Nov 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, I still think we should make sure removing it is the right choice. Why are we removing it? The button is especially useful on mobile devices where selecting the contents of an input isn't necessarily easy across devices (even my iPhone 12 Pro Max doesn't do a good job of selecting input fields to make it easy to copy or clear them).

Copy link
Member Author

@dhruvkb dhruvkb Nov 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the utility of the clear button on phones. It's good from a usability perspective. My main complaint against it was that it also appears on desktops, where its utility is doubtful. I'll let @panchovm take the call on this one.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I am confused, what are we removing?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I understand, sorry for the confusion. The clear action never was in the redesign, but you are right; it adds so much value to the search experience. I will work on adding it to the Design Library's components.

</style>
40 changes: 40 additions & 0 deletions src/components/Header/SearchBar/SearchButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<button
:type="type"
class="search-button flex items-center justify-center h-12 w-12 hover:text-white hover:bg-pink p-0.5px ps-1.5px focus:p-0 border border-s-0 focus:border-1.5 border-dark-charcoal-20 hover:border-pink focus:border-pink rounded-e-sm focus:outline-none"
v-on="$listeners"
>
<svg
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
aria-hidden="true"
focusable="false"
>
<use :href="`${searchIcon}#icon`" />
</svg>
</button>
</template>

<script>
import searchIcon from '~/assets/icons/search.svg'

export default {
name: 'SearchButton',
setup(props, { attrs }) {
const type = attrs['type'] ?? 'button'

return {
searchIcon,

type,
}
},
}
</script>

<style scoped>
.search-button:hover:focus {
box-shadow: 0 0 0 1px inset white;
}
</style>
71 changes: 71 additions & 0 deletions src/components/Header/SearchBar/meta/SearchBar.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {
ArgsTable,
Canvas,
Description,
Meta,
Story,
} from '@storybook/addon-docs'
import SearchBar from '~/components/Header/SearchBar/SearchBar.vue'

<Meta
title="Components/Header/Search bar"
component={SearchBar}
argTypes={{
input: {
action: 'input',
},
searched: {
action: 'searched',
},
}}
/>

export const Template = (args, { argTypes }) => ({
template: `<SearchBar v-bind="$props" v-on="$props" />`,
components: { SearchBar },
props: Object.keys(argTypes),
})

# Search bar

<Description of={SearchBar} />

<ArgsTable of={SearchBar} />

The component emits an `input` event with the new contents of the field whenever
the field receives an input. It also emits the `search` event when the search
button is clicked.

<Canvas>
<Story
name="Default"
args={{
value: 'Search query',
extraText: '12,345 results',
}}
>
{Template.bind({})}
</Story>
</Canvas>

The recommended way to use it is with `v-model` mapping to a `String`
representing the search query.

export const vModelTemplate = () => ({
template: `
<div>
<SearchBar v-model="text" :extra-text="text.length" />
{{ text }}
</div>
`,
components: { SearchBar },
data() {
return {
text: 'Hello, World!',
}
},
})

<Canvas>
<Story name="v-model">{vModelTemplate.bind({})}</Story>
</Canvas>
Loading