Skip to content

Commit

Permalink
Merge pull request #169 from pulibrary/lux-input-radio
Browse files Browse the repository at this point in the history
Adds luxInputRadio component
  • Loading branch information
christinach authored Mar 29, 2024
2 parents 5ec950d + c2468c3 commit a515a13
Show file tree
Hide file tree
Showing 5 changed files with 420 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ you will need to refactor it back into a regular template.
1. If a value is not updating correctly after a `wrapper.setProps` call, follow these steps to add a `nextTick()`, which will cause Vue to update the value:
1. If it's not already imported, add `import { nextTick } from "vue"`
2. Add `async` to the test arrow function.
3. Add `await nextTick()` between the `setProps` and the assertion.
3. Add `await nextTick()` between the `setProps` and the assertion, or simply prepend the setProps command with `await`. (Example: `await wrapper.setProps({ show: true })`)
1. Refer to the [vue-test-utils migration guide](https://test-utils.vuejs.org/migration/) for other breaking changes.
1. Running the tests will produce a snapshot file. Compare it to the original snapshot file. If there are no substantial changes, commit it. If there are substantial changes, make any necessary changes.

Expand Down
252 changes: 252 additions & 0 deletions src/components/LuxInputRadio.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
<template>
<component :is="wrapper" class="lux-input">
<legend v-if="groupLabel">{{ groupLabel }}</legend>
<div
v-for="option in options"
class="lux-radio"
:class="{ 'lux-inline': !vertical }"
:key="option.id"
>
<input
type="radio"
:id="option.id"
:name="option.name"
:value="option.value"
:checked="option.checked"
:disabled="option.disabled"
:required="option.required"
@change="change($event.target.value)"
@blur="inputblur($event.target)"
/>
<label v-if="option.label" :for="option.id">{{ option.label }}</label>
<label v-else :for="option.id">{{ option.value }}</label>
</div>
<div role="alert" class="lux-error" v-if="errormessage">{{ errormessage }}</div>
</component>
</template>

<script>
/**
* Radio buttons should only be used when a user can select one option.
* For multiple selections, use checkboxes.
*/
export default {
name: "LuxInputRadio",
status: "ready",
release: "1.0.0",
type: "Element",
data: function () {
return {
wrapper: this.groupLabel.length ? "fieldset" : "div",
}
},
props: {
/**
* If true, the radio buttons will be stacked vertically. Otherwise they will be horizontal (inline).
*/
vertical: {
type: Boolean,
default: false,
},
/**
* The available options to check. Option properties are: id, value, disabled, required, checked
*/
options: {
required: true,
type: Array,
},
/**
* The label of the form input field.
*/
groupLabel: {
type: String,
default: "",
},
/**
* The validation message a user should get.
*/
errormessage: {
type: String,
default: "",
},
/**
* Unique identifier of the form input field.
*/
id: {
type: String,
default: "",
required: true,
},
/**
* Whether the form input field is disabled or not.
* `true, false`
*/
disabled: {
type: Boolean,
default: false,
},
/**
* Whether the form input field is required or not.
* `true, false`
*/
required: {
type: Boolean,
default: false,
},
/**
* Manually trigger input field’s hover state.
* `true, false`
*/
hover: {
type: Boolean,
default: false,
},
/**
* Manually trigger input field’s focus state.
* `true, false`
*/
focus: {
type: Boolean,
default: false,
},
},
methods: {
change(value) {
this.$emit("change", value)
},
inputblur(value) {
this.$emit("inputblur", value)
},
},
}
</script>

<style lang="scss" scoped>
@import "../assets/styles/spacing.scss";
@import "../assets/styles/mixins.scss";
@import "../assets/styles/variables.css";
@import "../assets/styles/system.scss";
fieldset {
border: 0;
padding: 0;
}
.lux-input {
@include stack-space(var(--space-small));
font-weight: var(--font-weight-regular);
font-family: var(--font-family-text);
font-size: var(--font-size-base);
legend {
margin-bottom: var(--space-x-small);
}
label {
padding-right: var(--space-base);
}
}
.lux-radio {
@include reset;
@include stack-space(var(--space-x-small));
font-family: var(--font-family-text);
line-height: var(--line-height-base);
}
.lux-radio input[type="radio"] {
@include visually-hidden;
}
.lux-radio label {
position: relative;
display: inline-block;
margin-bottom: var(--space-xx-small);
cursor: pointer;
padding-left: var(--space-base);
}
.lux-radio label::before,
.lux-radio label::after {
position: absolute;
content: "";
/*Needed for the line-height to take effect*/
display: inline-block;
}
/*Outer box of the fake radio*/
.lux-radio label::before {
height: 16px;
width: 16px;
background-color: var(--color-white);
border: 0;
border-radius: var(--border-radius-circle);
box-shadow: inset 0 1px 0 0 rgba($color-rich-black, 0.07), 0 0 0 1px tint($color-rich-black, 80%);
left: 0;
top: 4px;
}
/* On mouse-over, add a grey background color */
.lux-radio :not([disabled]) + label:hover::before {
box-shadow: 0 1px 5px 0 rgba($color-rich-black, 0.07), 0 0 0 1px tint($color-rich-black, 60%);
}
.lux-radio input:checked + label::before {
transition: box-shadow 0.2s ease;
background-color: var(--color-bleu-de-france);
box-shadow: inset 0 0 0 1px var(--color-bleu-de-france), 0 0 0 1px var(--color-bleu-de-france);
outline: 0;
}
/*Checkmark of the fake radio*/
.lux-radio label::after {
height: 6px;
width: 6px;
border-radius: var(--border-radius-circle);
background-color: var(--color-white);
left: 5px;
top: 9px;
}
/*Hide the checkmark by default*/
.lux-radio input[type="radio"] + label::after {
content: none;
}
/*Unhide on the checked state*/
.lux-radio input[type="radio"]:checked + label::after {
content: "";
}
/*Adding focus styles on the outer-box of the fake radio*/
.lux-radio input[type="radio"]:focus + label::before {
transition: box-shadow 0.2s ease;
box-shadow: inset 0 0 0 1px var(--color-bleu-de-france), 0 0 0 1px var(--color-bleu-de-france);
outline: 0;
}
.lux-inline {
display: inline-block;
}
[disabled] + label {
cursor: not-allowed;
color: var(--color-grayscale);
}
</style>

<docs>
```jsx
<div>
<lux-input-radio
id="foo"
vertical groupLabel="Where is my mind?"
:options="[
{name: 'radio-group-name', value: 'In the clouds', id: 'radio-opt1', required: true},
{name: 'radio-group-name', value: 'I don\'t know', id: 'radio-opt2', disabled: true}
]">
</lux-input-radio>
</div>
```
</docs>
2 changes: 2 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./icons"
import LuxInputButton from "./LuxInputButton.vue"
import LuxInputCheckbox from "./LuxInputCheckbox.vue"
import LuxInputRadio from "./LuxInputRadio.vue"
import LuxInputSelect from "./LuxInputSelect.vue"
import LuxInputText from "./LuxInputText.vue"
import _LuxCardContent from "./_LuxCardContent.vue"
Expand Down Expand Up @@ -36,6 +37,7 @@ import LuxTag from "./LuxTag.vue"
export {
LuxInputButton,
LuxInputCheckbox,
LuxInputRadio,
LuxInputSelect,
LuxInputText,
_LuxCardContent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`LuxInputRadio.vue has the expected html structure 1`] = `
<div
class="lux-input"
>
<!--v-if-->
<div
class="lux-radio lux-inline"
>
<input
id="radio-opt1"
name="opt 1"
type="radio"
value="one"
/>
<label
for="radio-opt1"
>
one
</label>
</div>
<div
class="lux-radio lux-inline"
>
<input
id="radio-opt2"
name="opt 2"
type="radio"
value="two"
/>
<label
for="radio-opt2"
>
two
</label>
</div>
<!--v-if-->
</div>
`;
Loading

0 comments on commit a515a13

Please sign in to comment.