feat(frontend-shared): unifying frontend components, build tools, styles, test config, and more. (#18265)

* feat(frontend-shared): Adding shared components, styles, test tooling, build tooling, and plugins

* chore(launchpad): fix build for feat/fronted-shared-infra (#18267)

* wip: fix build

* fix tests

* modify tsconfig

* fix typing error

* simplify imports

Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
This commit is contained in:
Jessica Sachs
2021-09-28 03:48:18 -04:00
committed by GitHub
parent 377ef0aae9
commit 338da82961
122 changed files with 2178 additions and 1201 deletions
+142 -79
View File
@@ -1,79 +1,142 @@
<p align="center">
<img src="https://cloud.githubusercontent.com/assets/1268976/20607953/d7ae489c-b24a-11e6-9cc4-91c6c74c5e88.png"/>
</p>
<p align="center">
<a href="https://on.cypress.io">Documentation</a> |
<a href="https://on.cypress.io/changelog">Changelog</a> |
<a href="https://on.cypress.io/roadmap">Roadmap</a>
</p>
<h3 align="center">
The web has evolved. Finally, testing has too.
</h3>
<p align="center">
Fast, easy and reliable testing for anything that runs in a browser.
</p>
<p align="center">
Join us, we're <a href="https://cypress.io/jobs">hiring</a>.
</p>
<p align="center">
<a href="https://www.npmjs.com/package/cypress">
<img src="https://img.shields.io/npm/dm/cypress.svg" alt="npm"/>
</a>
<a href="https://gitter.im/cypress-io/cypress">
<img src="https://img.shields.io/gitter/room/cypress-io/cypress.svg" alt="Gitter chat"/>
</a>
<a href="https://stackshare.io/cypress">
<img src="https://img.stackshare.io/misc/follow-on-stackshare-badge.svg" alt="StackShare"/>
</a><br />
</p>
## What is Cypress?
<p align="center">
<a href="https://player.vimeo.com/video/237527670">
<img alt="Why Cypress Video" src="https://user-images.githubusercontent.com/1271364/31739717-dbdff0ee-b41c-11e7-9b16-bfa1b6ac1814.png" width="75%" height="75%" />
</a>
</p>
## Installing
[![npm version](https://badge.fury.io/js/cypress.svg)](https://badge.fury.io/js/cypress)
Install Cypress for Mac, Linux, or Windows, then [get started](https://docs.cypress.io/guides/getting-started/installing-cypress.html).
```bash
npm install cypress --save-dev
```
or
```bash
yarn add cypress --dev
```
![installing-cli e1693232](https://user-images.githubusercontent.com/1271364/31740846-7bf607f0-b420-11e7-855f-41c996040d31.gif)
## Contributing
- [![CircleCI](https://circleci.com/gh/cypress-io/cypress/tree/develop.svg?style=svg)](https://circleci.com/gh/cypress-io/cypress/tree/develop) - `develop` branch
- [![CircleCI](https://circleci.com/gh/cypress-io/cypress/tree/master.svg?style=svg)](https://circleci.com/gh/cypress-io/cypress/tree/master) - `master` branch
Please see our [Contributing Guideline](./CONTRIBUTING.md) which explains repo organization, linting, testing, and other steps.
## License
[![license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/cypress-io/cypress/blob/master/LICENSE)
This project is licensed under the terms of the [MIT license](/LICENSE).
## Badges
Let the world know your project is using Cypress.io to test with this cool badge
[![Cypress.io](https://img.shields.io/badge/tested%20with-Cypress-04C38E.svg)](https://www.cypress.io/)
```
[![Cypress.io](https://img.shields.io/badge/tested%20with-Cypress-04C38E.svg)](https://www.cypress.io/)
```
<p align="center">
<a href="" rel="noopener">
<img width=200px height=200px src="https://i.imgur.com/FxL5qM0.jpg" alt="Bot logo"></a>
</p>
<h3 align="center">cypress</h3>
<div align="center">
[![Status](https://img.shields.io/badge/status-active-success.svg)]()
[![Platform](https://img.shields.io/badge/platform-reddit-orange.svg)](https://www.reddit.com/user/Wordbook_Bot)
[![GitHub Issues](https://img.shields.io/github/issues/kylelobo/The-Documentation-Compendium.svg)](https://github.com/kylelobo/The-Documentation-Compendium/issues)
[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/kylelobo/The-Documentation-Compendium.svg)](https://github.com/kylelobo/The-Documentation-Compendium/pulls)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](/LICENSE)
</div>
---
<p align="center"> 🤖 Few lines describing what your bot does.
<br>
</p>
## 📝 Table of Contents
- [About](#about)
- [Demo / Working](#demo)
- [How it works](#working)
- [Usage](#usage)
- [Getting Started](#getting_started)
- [Deploying your own bot](#deployment)
- [Built Using](#built_using)
- [TODO](../TODO.md)
- [Contributing](../CONTRIBUTING.md)
- [Authors](#authors)
- [Acknowledgments](#acknowledgement)
## 🧐 About <a name = "about"></a>
Write about 1-2 paragraphs describing the purpose of your bot.
## 🎥 Demo / Working <a name = "demo"></a>
![Working](https://media.giphy.com/media/20NLMBm0BkUOwNljwv/giphy.gif)
## 💭 How it works <a name = "working"></a>
The bot first extracts the word from the comment and then fetches word definitions, part of speech, example and source from the Oxford Dictionary API.
If the word does not exist in the Oxford Dictionary, the Oxford API then returns a 404 response upon which the bot then tries to fetch results form the Urban Dictionary API.
The bot uses the Pushshift API to fetch comments, PRAW module to reply to comments and Heroku as a server.
The entire bot is written in Python 3.6
## 🎈 Usage <a name = "usage"></a>
To use the bot, type:
```
!dict word
```
The first part, i.e. "!dict" **is not** case sensitive.
The bot will then give you the Oxford Dictionary (or Urban Dictionary; if the word does not exist in the Oxford Dictionary) definition of the word as a comment reply.
### Example:
> !dict what is love
**Definition:**
Baby, dont hurt me~
Dont hurt me~ no more.
**Example:**
Dude1: Bruh, what is love?
Dude2: Baby, dont hurt me, dont hurt me- no more!
Dude1: dafuq?
**Source:** https://www.urbandictionary.com/define.php?term=what%20is%20love
---
<sup>Beep boop. I am a bot. If there are any issues, contact my [Master](https://www.reddit.com/message/compose/?to=PositivePlayer1&subject=/u/Wordbook_Bot)</sup>
<sup>Want to make a similar reddit bot? Check out: [GitHub](https://github.com/kylelobo/Reddit-Bot)</sup>
## 🏁 Getting Started <a name = "getting_started"></a>
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See [deployment](#deployment) for notes on how to deploy the project on a live system.
### Prerequisites
What things you need to install the software and how to install them.
```
Give examples
```
### Installing
A step by step series of examples that tell you how to get a development env running.
Say what the step will be
```
Give the example
```
And repeat
```
until finished
```
End with an example of getting some data out of the system or using it for a little demo.
## 🚀 Deploying your own bot <a name = "deployment"></a>
To see an example project on how to deploy your bot, please see my own configuration:
- **Heroku**: https://github.com/kylelobo/Reddit-Bot#deploying_the_bot
## ⛏️ Built Using <a name = "built_using"></a>
- [PRAW](https://praw.readthedocs.io/en/latest/) - Python Reddit API Wrapper
- [Heroku](https://www.heroku.com/) - SaaS hosting platform
## ✍️ Authors <a name = "authors"></a>
- [@kylelobo](https://github.com/kylelobo) - Idea & Initial work
See also the list of [contributors](https://github.com/kylelobo/The-Documentation-Compendium/contributors) who participated in this project.
## 🎉 Acknowledgements <a name = "acknowledgement"></a>
- Hat tip to anyone whose code was used
- Inspiration
- References
+1 -1
View File
@@ -17,7 +17,7 @@ interface RuleConfig {
}
const makeRuleForClass = ({ name, theme, weight, color }: RuleConfig) => {
const resolvedColor = color ? color : weight ? theme(`colors.${name}.${weight}`) : theme(`colors.${name}`)
const resolvedColor = color ? color : weight ? theme?.(`colors.${name}.${weight}`) : theme?.(`colors.${name}`)
let [lightKey, darkKey] = [`.icon-light-${name}`, `.icon-dark-${name}`]
// transparent, black, and white
+1 -2
View File
@@ -1,4 +1,4 @@
import { registerMountFn } from '@packages/frontend-shared/cypress/support/commands'
import { registerMountFn } from '@packages/frontend-shared/cypress/support/common'
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
@@ -19,7 +19,6 @@ import { registerMountFn } from '@packages/frontend-shared/cypress/support/comma
import 'virtual:windi.css'
import '../../src/main.scss'
import '@iconify/iconify'
import '@purge-icons/generated'
import { createRouter } from '../../src/router/router'
registerMountFn({ plugins: [() => createRouter()] })
+1
View File
@@ -26,6 +26,7 @@
"@iconify/json": "1.1.368",
"@iconify/vue": "3.0.0-beta.1",
"@intlify/vite-plugin-vue-i18n": "2.4.0",
"@packages/frontend-shared": "0.0.0-development",
"@testing-library/cypress": "8.0.0",
"@urql/core": "2.3.1",
"@urql/vue": "0.4.3",
-119
View File
@@ -1,119 +0,0 @@
{
"links": {
"learnMore": "Learn more."
},
"components": {
"modal": {
"dismiss": "Dismiss"
},
"select": {
"placeholder": "Choose an option..."
}
},
"clipboard": {
"copy": "Copy",
"copied": "Copied"
},
"file": {
"edit": "Edit"
},
"status": {
"enabled": "Enabled",
"disabled": "Disabled"
},
"setupPage": {
"projectSetup": {
"frameworkLabel": "Front-end Framework",
"frameworkPlaceholder": "Pick a framework",
"bundlerLabel": "Bundler",
"bundlerPlaceholder": "Pick a bundler"
},
"install": {
"startButton": "Install",
"confirmManualInstall": "I've installed them"
},
"step": {
"next": "Next Step",
"back": "Back"
}
},
"globalPage": {
"empty": {
"title": "Welcome to Cypress!",
"helper": "Add your first project below to start testing with Cypress.",
"dropText": "Drag your project here or {0}",
"browseManually": "browse manually."
},
"searchPlaceholder": "Search Projects",
"newProjectButton": "New Project"
},
"welcomeGuide": {
"header": {
"title": "Cypress",
"description": "A powerful open-source testing platform for developers and engineering teams all around the world."
},
"projectListHeader": "Recently Viewed Projects",
"linkHeader": "Quick Access",
"confirmWelcomeGuide": "Show the welcome guide when opening Cypress."
},
"settingsPage": {
"config": {
"title": "Resolved Configuration",
"description": "Since the options in the {0} can be set dynamically by your development environment, please refer to the legend below to understand how the configuration options are resolved.",
"legend": {
"env": {
"label": "env",
"description": "Set from environment variables"
},
"default": {
"label": "default",
"description": "Default values"
},
"dynamic": {
"label": "dynamic",
"description": "Set by the {0} function"
},
"cli": {
"label": "cli",
"description": "Set from CLI arguments"
},
"config": {
"label": "config",
"description": "Set from {0}"
}
}
},
"proxy": {
"title": "Proxy Settings",
"description": "Cypress auto-detected the following proxy settings from your operating system.",
"bypassList": "Proxy Bypass List",
"proxyServer": "Proxy Server"
},
"editor": {
"title": "External Editor",
"description": "External editor to use for editing code opened when using Cypress.",
"noEditorSelectedPlaceholder": "Choose your editor..."
},
"projectId": {
"title": "Project ID",
"description": "The Project ID configured for this project inside of Cypress Cloud. {0}"
},
"recordKey": {
"title": "Record Key",
"description": "The Record Key configured for this project inside of Cypress Cloud. {0}",
"manageKeys": "Manage Keys"
},
"project": {
"title": "Project Settings",
"description": "Settings for this project."
},
"experiments": {
"title": "Experiments",
"description": "If you'd like to try out new features that we're working on, you can enable beta features for your project by turning on the experimental features you'd like to try. {0}"
},
"device": {
"title": "Device Settings",
"description": "Settings for this device."
}
}
}
+1 -266
View File
@@ -1,21 +1,4 @@
/* Define the "system" font family */
@font-face {
font-family: system;
font-style: normal;
font-weight: 300;
src: local(".SFNSText-Light"), local(".HelveticaNeueDeskInterface-Light"),
local(".LucidaGrandeUI"), local("Ubuntu Light"), local("Segoe UI Light"),
local("Roboto-Light"), local("DroidSans"), local("Tahoma");
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
/**
* Animations
* -------------------------
*/
@use '@packages/frontend-shared/src/styles/shared.scss';
.fade-enter-active,
.fade-leave-active {
@@ -42,251 +25,3 @@ body {
height: 0;
}
}
/**
* Global Resets
* -------------------------
*/
// Input Resets
// Taken from Tailwind CSS Forms -- a plugin for tailwind.
// We could probably add these into WindiCSS directly
// https://github.com/tailwindlabs/tailwindcss-forms
[multiple],
[type=date],
[type=datetime-local],
[type=email],
[type=month],
[type=number],
[type=password],
[type=search],
[type=tel],
[type=text],
[type=time],
[type=url],
[type=week],
select,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
border-radius: 0;
padding-top: 0.35rem;
padding-bottom: 0.35rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
line-height: 1.25rem;
}
[multiple]:focus,
[type=date]:focus,
[type=datetime-local]:focus,
[type=email]:focus,
[type=month]:focus,
[type=number]:focus,
[type=password]:focus,
[type=search]:focus,
[type=tel]:focus,
[type=text]:focus,
[type=time]:focus,
[type=url]:focus,
[type=week]:focus,
select:focus,
textarea:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty, );
/*!*/
/*!*/
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
border-color: #2563eb;
}
input::-moz-placeholder,
textarea::-moz-placeholder {
color: #6b7280;
opacity: 1;
}
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #6b7280;
opacity: 1;
}
input::placeholder,
textarea::placeholder {
color: #6b7280;
opacity: 1;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-date-and-time-value {
min-height: 1.5em;
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right .5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
[multiple] {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: .75rem;
-webkit-print-color-adjust: unset;
color-adjust: unset;
}
[type=checkbox],
[type=radio] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
color: #2563eb;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
}
[type=checkbox] {
border-radius: 0;
}
[type=radio] {
border-radius: 100%;
}
[type=checkbox]:focus,
[type=radio]:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty);
--tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
}
[type=checkbox]:checked,
[type=radio]:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type=checkbox]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
[type=radio]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
}
[type=checkbox]:checked:focus,
[type=checkbox]:checked:hover,
[type=radio]:checked:focus,
[type=radio]:checked:hover {
border-color: transparent;
background-color: currentColor;
}
[type=checkbox]:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type=checkbox]:indeterminate:focus,
[type=checkbox]:indeterminate:hover {
border-color: transparent;
background-color: currentColor;
}
[type=file] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit;
}
[type=file]:focus {
outline: 1px auto -webkit-focus-ring-color;
}
input[type="search"].dark {
background: #222;
color: #fff;
}
input[type="search"].light {
background: #fff;
color: #222;
}
input[type="search"].suffix::-webkit-search-cancel-button {
@apply pr-3.25em;
}
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
margin: 1em;
height: 1em;
width: 1em;
border-radius: 50em;
background: url(https://pro.fontawesome.com/releases/v5.10.0/svgs/solid/times-circle.svg)
no-repeat 50% 50%;
background-size: contain;
opacity: 0;
pointer-events: none;
}
input[type="search"]:focus::-webkit-search-cancel-button {
opacity: 0.3;
pointer-events: all;
}
input[type="search"].dark::-webkit-search-cancel-button {
filter: invert(1);
}
+1 -1
View File
@@ -4,7 +4,7 @@ import 'virtual:windi.css'
import urql from '@urql/vue'
import App from './App.vue'
import { makeUrqlClient } from '@packages/frontend-shared/src/graphql/urqlClient'
import { createI18n } from './locales/i18n'
import { createI18n } from '@cy/i18n'
import { createRouter } from './router/router'
const app = createApp(App)
+16 -1
View File
@@ -1,4 +1,19 @@
{
"extends": "../frontend-shared/tsconfig.json",
"include": ["src/**/*.vue", "src/**/*.tsx", "cypress/**/*.ts"]
"include": [
"src/**/*.vue",
"src/**/*.tsx",
"cypress/**/*.ts",
"cypress/**/*.tsx",
"../frontend-shared/src/**/*.vue",
"../frontend-shared/src/**/*.tsx",
"../frontend-shared/cypress/**/*.ts"
],
"compilerOptions": {
"paths": {
"@cy/i18n": ["../frontend-shared/src/locales/i18n"],
"@cy/components/*": ["../frontend-shared/src/components/*"],
"@packages/*": ["../*"]
}
}
}
+3 -44
View File
@@ -1,51 +1,10 @@
import path from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import WindiCSS from 'vite-plugin-windicss'
import PurgeIcons from 'vite-plugin-purge-icons'
import VueI18n from '@intlify/vite-plugin-vue-i18n'
import VueSvgLoader from 'vite-svg-loader'
import { makeConfig } from '../frontend-shared/vite.config'
import Layouts from 'vite-plugin-vue-layouts'
import Pages from 'vite-plugin-pages'
import Components from 'unplugin-vue-components/vite'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
export default defineConfig({
base: './',
build: {
minify: false,
},
export default makeConfig({}, {
plugins: [
vue(),
vueJsx(),
Layouts(),
Pages({
extensions: ['vue'],
}),
PurgeIcons(),
VueI18n({
include: path.resolve(__dirname, './src/locales/**'),
}),
Icons({
customCollections: {
// ~icons/cy/book-x16
cy: FileSystemIconLoader('./src/assets/icons'),
},
}),
Components({
// <i-cy-book-x16/>
resolvers: IconsResolver({
customCollections: ['cy'],
}),
}),
WindiCSS(),
VueSvgLoader(),
Pages({ extensions: ['vue'] }),
],
define: {
'process.env': {},
'setImmediate': {},
},
})
+2 -31
View File
@@ -1,32 +1,3 @@
import { defineConfig } from 'windicss/helpers'
import InteractionVariants from '@windicss/plugin-interaction-variants'
import { IconDuotoneColorsPlugin } from './.windicss/icon-color-plugins'
import { safelist } from './.windicss/safelist'
import WindiConfig from '@packages/frontend-shared/windi.config'
export default defineConfig({
// This adds !important to all utility classes. https://csswizardry.com/2016/05/the-importance-of-important/
important: true,
theme: {
extend: {
gridTemplateColumns: {
launchpad: '64px 1fr',
},
gridTemplateRows: {
launchpad: '64px 1fr',
},
},
},
safelist,
variants: {
backgroundColor: ['group-focus-within', 'group-focus-visible', 'group-active', 'group-visited', 'group-disabled', 'hocus', 'group-hocus', 'can-hover', 'no-hover'],
},
plugins: [
InteractionVariants,
IconDuotoneColorsPlugin,
],
extract: {
// accepts globs and file paths relative to project root
include: ['index.html', 'src/**/*.{vue,html,tsx}'],
exclude: ['node_modules/**/*', '.git/**/*'],
},
})
export default WindiConfig
@@ -1,4 +1,6 @@
const colors = {
import Colors from 'windicss/colors'
const customColors = {
jade: {
50: '#E0F6FA',
100: '#C2F1DE',
@@ -144,46 +146,48 @@ const colors = {
},
}
export const customColors = {
...colors,
export const cyColors = {
...customColors,
primary: {
...colors.indigo,
DEFAULT: colors.indigo[500],
...customColors.indigo,
DEFAULT: customColors.indigo[500],
},
secondary: {
...colors.indigo,
DEFAULT: colors.indigo[50],
...customColors.indigo,
DEFAULT: customColors.indigo[50],
},
error: {
...colors.red,
DEFAULT: colors.red[400],
...customColors.red,
DEFAULT: customColors.red[400],
},
caution: {
...colors.red,
DEFAULT: colors.red[500],
...customColors.red,
DEFAULT: customColors.red[500],
},
warning: {
...colors.orange,
DEFAULT: colors.orange[500],
...customColors.orange,
DEFAULT: customColors.orange[500],
},
'warning-light': {
...colors.orange,
DEFAULT: colors.orange[400],
...customColors.orange,
DEFAULT: customColors.orange[400],
},
success: {
...colors.jade,
DEFAULT: colors.jade[400],
...customColors.jade,
DEFAULT: customColors.jade[400],
},
'success-light': {
...colors.jade,
DEFAULT: colors.jade[300],
...customColors.jade,
DEFAULT: customColors.jade[300],
},
confirm: {
...colors.jade,
DEFAULT: colors.jade[500],
...customColors.jade,
DEFAULT: customColors.jade[500],
},
'body-gray': {
...colors.gray,
DEFAULT: colors.gray[600],
...customColors.gray,
DEFAULT: customColors.gray[600],
},
}
export const colors = { ...Colors, ...cyColors }
@@ -0,0 +1,108 @@
/**
* This package adds support for targeting
* light vs dark classes in duotone icons.
*
* It works by adding utility classes and specific selectors
*/
import createPlugin from 'windicss/plugin'
import { reduce, kebabCase, isObject } from 'lodash'
import Colors from 'windicss/colors'
interface RuleConfig {
name: string
theme?: (key: string) => string
weight?: string
color?: string
}
const makeRuleForClass = ({ name, theme, weight, color }: RuleConfig) => {
const resolvedColor = color ? color : weight ? theme?.(`colors.${name}.${weight}`) : theme?.(`colors.${name}`)
let [lightKey, darkKey] = [`.icon-light-${name}`, `.icon-dark-${name}`]
// transparent, black, and white
if (weight) {
lightKey += `-${weight}`
darkKey += `-${weight}`
}
return {
// When we're targeting an svg with icon-light-red-500
// only attach the fill and stroke for those same icons
// and vice versa for icon-dark
[lightKey]: {
'> *[fill].icon-light': {
fill: resolvedColor,
},
'> *[stroke].icon-light': {
stroke: resolvedColor,
},
'> *[fill][stroke].icon-light-fill': {
fill: resolvedColor,
},
},
[darkKey]: {
'> *[fill].icon-dark-fill': {
fill: resolvedColor,
},
'> *[fill].icon-dark': {
fill: resolvedColor,
},
'> *[stroke].icon-dark': {
stroke: resolvedColor,
},
'> *[fill][stroke].icon-dark-stroke': {
stroke: resolvedColor,
},
},
}
}
function addIconUtilityClasses (theme) {
return reduce(Colors, (acc, variants, colorName) => {
// lightGray => light-gray
const name = kebabCase(colorName)
// Collect the classes we're going to add to the windicss class registry
let additionalClasses = {}
// There are both nested and not-nested colors (e.g. black, white)
if (isObject(variants)) {
// multiple levels of colors
additionalClasses = reduce(variants, (variantAcc, _, weight) => {
const rules = makeRuleForClass({ name, theme, weight })
return { ...variantAcc, ...rules }
}, {})
} else {
// single values like black, white
additionalClasses = makeRuleForClass({ name, theme })
}
// Output is an object where each new class is a key
// And the selectors and values affected are values
/**
* {
* `.icon-light-green-500`: {
* '> *[stroke].icon-light': {
* stroke: resolvedColor
* },
* '> *[fill].icon-light': {
* fill: resolvedColor
* }
* }
* }
*/
return { ...acc, ...additionalClasses }
}, {
// These technically aren't under "colors"
...makeRuleForClass({ name: 'transparent', color: 'transparent' }),
...makeRuleForClass({ name: 'current', color: 'currentColor' }),
})
}
export const IconDuotoneColorsPlugin = createPlugin(({ theme, addUtilities }) => {
// @ts-ignore - dunno
addUtilities(addIconUtilityClasses(theme))
})
@@ -0,0 +1,22 @@
/**
* WindiCSS will strip out any styles that aren't used.
* We do a lot of dynamic stuff, and we're not too concerned
* ith bundle size, so this is a pretty greedy list
*/
import { colors } from './colors'
import { map, reduce, kebabCase } from 'lodash'
const textSafelist = ['xs', 'sm', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl'].map((v) => `text-${v}`).join(' ')
const colorSafelist = reduce(colors, (acc, variants, colorName) => {
const name = kebabCase(colorName)
return `${acc}
${map(variants, (_: string, k: string) => {
return `bg-${name}-${k}
text-${name}-${k}
before:bg-${name}-${k}
before:text-${name}-${k}`
}).join(' ')}`
}, '')
export const safelist = `${textSafelist} ${colorSafelist}`
+31
View File
@@ -0,0 +1,31 @@
# Frontend Shared
## Building
### For development
We only use Cypress Component Tests to develop the components in isolation
```bash
## from repo root
yarn workspace @packages/frontend-shared cypress:open
```
## Developing
For the best development experience, you will want to use VS Code with the [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) extension. This will give you type completion inside `vue` files.
## Testing
```bash
yarn workspace @packages/frontend-shared test-unit
```
## Utility class usage
Windi CSS can create an awesome interactive summary showing our usage of utility classes and design tokens. Running this command will generate this report and serve it on localhost.
```bash
## from this directory
yarn windi
```
+23
View File
@@ -0,0 +1,23 @@
{
"projectId": "sehy69",
"baseUrl": "http://localhost:5555",
"viewportWidth": 800,
"viewportHeight": 850,
"video": false,
"retries": {
"runMode": 2,
"openMode": 0
},
"nodeVersion": "system",
"testFiles": "**/*.spec.{js,ts,tsx,jsx}",
"reporter": "../../node_modules/cypress-multi-reporters/index.js",
"reporterOptions": {
"configFile": "../../mocha-reporter-config.json"
},
"component": {
"componentFolder": "src/components"
},
"e2e": {
"supportFile": false
}
}
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
@@ -0,0 +1,42 @@
/**
* @type {import('@cypress/vite-dev-server')}
*/
const { startDevServer } = require('@cypress/vite-dev-server')
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
if (config.testingType === 'component') {
on('dev-server:start', async (options) => {
return startDevServer({
options,
viteConfig: {
// TODO(tim): Figure out why this isn't being picked up
optimizeDeps: {
include: ['@headlessui/vue', 'vue-prism-component'],
},
},
})
})
}
return config // IMPORTANT to return a config
}
@@ -15,13 +15,14 @@ import { navigationMenu } from '../../src/graphql/testNavigationMenu'
import { query as stubQuery } from '../../src/graphql/testQuery'
import { wizard as stubWizard } from '../../src/graphql/testWizard'
import { app as stubApp } from '../../src/graphql/testApp'
import { createI18n } from '@cy/i18n'
/**
* This variable is mimicing ipc provided by electron.
* It has to be loaded run before initializing GraphQL
* because graphql uses it.
*/
(window as any).ipc = {
;(window as any).ipc = {
on: () => {},
send: () => {},
}
@@ -37,7 +38,11 @@ const createContext = (): ClientTestContext => {
}
}
export const registerMountFn = ({ plugins }) => {
export interface MountFnOptions {
plugins?: (() => any)[]
}
export const registerMountFn = ({ plugins }: MountFnOptions = {}) => {
Cypress.Commands.add(
'mount',
<C extends Parameters<typeof mount>[0]>(comp: C, options: CyMountOptions<C> = {}) => {
@@ -49,6 +54,8 @@ export const registerMountFn = ({ plugins }) => {
options?.global?.plugins?.push(pluginFn())
})
options.global.plugins.push(createI18n())
const context = createContext()
options.global.plugins.push({
@@ -75,6 +82,7 @@ export const registerMountFn = ({ plugins }) => {
transition: false,
},
plugins: [
createI18n(),
{
install (app) {
app.use(urql, testUrqlClient({
@@ -0,0 +1,5 @@
import { registerMountFn } from './common'
import '../../src/styles/shared.scss'
import 'virtual:windi.css'
registerMountFn()
+30 -3
View File
@@ -4,29 +4,56 @@
"private": true,
"scripts": {
"build-prod": "tsc || echo 'built, with type errors'",
"check-ts": "tsc --noEmit",
"check-ts": "vue-tsc --noEmit",
"clean-deps": "rimraf node_modules",
"clean": "rimraf src/*.js src/**/*.js"
"clean": "rimraf dist ./node_modules/.vite src/*.js src/**/*.js && echo 'cleaned'",
"test": "yarn cypress:run && yarn types",
"windi": "yarn windicss-analysis",
"cypress:open": "cross-env TZ=America/New_York node ../../scripts/cypress open-ct --project ${PWD}",
"cypress:run": "cross-env TZ=America/New_York node ../../scripts/cypress run-ct --project ${PWD}",
"dev": "vite --open"
},
"dependencies": {},
"devDependencies": {
"@antfu/utils": "^0.3.0",
"@cypress/vite-dev-server": "0.0.0-development",
"@cypress/vue": "0.0.0-development",
"@headlessui/vue": "1.4.0",
"@iconify/json": "1.1.368",
"@iconify/vue": "3.0.0-beta.1",
"@intlify/vite-plugin-vue-i18n": "2.4.0",
"@testing-library/cypress": "8.0.0",
"@urql/core": "2.3.1",
"@urql/exchange-execute": "1.1.0",
"@urql/exchange-graphcache": "4.3.3",
"@urql/vue": "0.4.3",
"@vitejs/plugin-vue": "^1.9.2",
"@vitejs/plugin-vue-jsx": "^1.1.8",
"@vue/compiler-core": "3.2.6",
"@vue/compiler-dom": "3.2.6",
"@vue/compiler-sfc": "3.2.6",
"@windicss/plugin-interaction-variants": "1.0.0",
"bluebird": "3.5.3",
"classnames": "2.3.1",
"fake-uuid": "^1.0.0",
"graphql": "^15.5.1",
"graphql-relay": "^0.9.0",
"graphql-tag": "^2.12.5",
"lodash": "^4.17.21",
"modern-normalize": "1.1.0",
"rimraf": "3.0.2",
"unplugin-icons": "^0.11.4",
"unplugin-vue-components": "^0.15.4",
"vite": "^2.5.10",
"vite-plugin-icons": "0.6.3",
"vite-plugin-windicss": "^1.4.7",
"vite-svg-loader": "^2.2.0",
"vue": "3.2.6",
"vue-eslint-parser": "7.11.0",
"vue-i18n": "9.2.0-beta.1",
"vue-i18n": "^9.2.0-beta.7",
"vue-tsc": "^0.3.0",
"windicss": "3.1.8",
"windicss-analysis": "^0.3.4",
"wonka": "^4.0.15"
}
}

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 476 B

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 476 B

Before

Width:  |  Height:  |  Size: 440 B

After

Width:  |  Height:  |  Size: 440 B

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 650 B

After

Width:  |  Height:  |  Size: 650 B

Before

Width:  |  Height:  |  Size: 658 B

After

Width:  |  Height:  |  Size: 658 B

Before

Width:  |  Height:  |  Size: 597 B

After

Width:  |  Height:  |  Size: 597 B

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 755 B

Before

Width:  |  Height:  |  Size: 763 B

After

Width:  |  Height:  |  Size: 763 B

Before

Width:  |  Height:  |  Size: 734 B

After

Width:  |  Height:  |  Size: 734 B

Before

Width:  |  Height:  |  Size: 760 B

After

Width:  |  Height:  |  Size: 760 B

Before

Width:  |  Height:  |  Size: 751 B

After

Width:  |  Height:  |  Size: 751 B

Before

Width:  |  Height:  |  Size: 743 B

After

Width:  |  Height:  |  Size: 743 B

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 768 B

Before

Width:  |  Height:  |  Size: 751 B

After

Width:  |  Height:  |  Size: 751 B

Before

Width:  |  Height:  |  Size: 435 B

After

Width:  |  Height:  |  Size: 435 B

Before

Width:  |  Height:  |  Size: 424 B

After

Width:  |  Height:  |  Size: 424 B

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -1,5 +1,5 @@
import Button from './Button.vue'
import IconCoffee from 'virtual:vite-icons/mdi/coffee'
import IconCoffee from '~icons/mdi/coffee'
describe('<Button />', () => {
it('playground', { viewportWidth: 200, viewportHeight: 300 }, () => {
@@ -35,13 +35,21 @@
</template>
<script lang="ts">
export default {
import { defineComponent } from 'vue'
export default defineComponent({
inheritAttrs: true,
}
})
</script>
<script lang="ts" setup>
import { computed, useAttrs, ButtonHTMLAttributes, FunctionalComponent, SVGAttributes } from 'vue'
import Icon from './Icon.vue'
// eslint-disable-next-line no-duplicate-imports
import { computed, useAttrs } from 'vue'
// eslint-disable-next-line no-duplicate-imports
import type { ButtonHTMLAttributes, FunctionalComponent, SVGAttributes } from 'vue'
const VariantClassesTable = {
primary: 'border-indigo-600 bg-indigo-600 text-white',
@@ -22,7 +22,7 @@
<script lang="ts">
import { defineComponent, nextTick, ref } from 'vue'
import { useI18n } from '../../composables'
import { useI18n } from '@cy/i18n'
export default defineComponent({
props: {
@@ -1,6 +1,6 @@
import Icon from './Icon.vue'
import IconCoffee from 'virtual:vite-icons/mdi/coffee'
import IconHeart from 'virtual:vite-icons/mdi/heart'
import IconCoffee from '~icons/mdi/coffee'
import IconHeart from '~icons/mdi/heart'
describe('<Icon />', () => {
it('handles an icon SVG', () => {
@@ -55,10 +55,14 @@ export default {
</script>
<script lang="ts" setup>
import { computed, useSlots, useAttrs, InputHTMLAttributes, FunctionalComponent, SVGAttributes } from 'vue'
import _ from 'lodash'
import IconSearch from 'virtual:vite-icons/mdi/magnify'
import { useModelWrapper } from '../../composables'
import Icon from './Icon.vue'
import IconSearch from '~icons/mdi/magnify'
import type { InputHTMLAttributes, FunctionalComponent, SVGAttributes } from 'vue'
// eslint-disable-next-line no-duplicate-imports
import { computed, useSlots, useAttrs } from 'vue'
import { useModelWrapper } from '../composables'
const slots = useSlots()
const attrs = useAttrs()
@@ -1,13 +1,13 @@
import Icon from '../icon/Icon.vue'
import Icon from './Icon.vue'
import IconHeart from '~icons/mdi/heart'
import { defaultMessages } from '@cy/i18n'
import { h } from 'vue'
import IconHeart from 'virtual:vite-icons/mdi/heart'
import { defaultMessages } from '../../locales/i18n'
const selectText = defaultMessages.components.select
// Subject Under Test
import Select from './Select.vue'
const selectText = defaultMessages.components.select
// Selectors
const optionsSelector = '[role=option]'
const inputSelector = '[aria-haspopup=true]'
@@ -141,11 +141,11 @@
<script lang="ts" setup>
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '@headlessui/vue'
import IconCheck from 'virtual:vite-icons/mdi/check'
import IconCaret from 'virtual:vite-icons/mdi/caret'
import Icon from '../icon/Icon.vue'
import IconCheck from '~icons/mdi/check'
import IconCaret from '~icons/mdi/caret'
import Icon from './Icon.vue'
import { get } from 'lodash'
import { useI18n } from '../../composables'
import { useI18n } from '@cy/i18n'
const { t } = useI18n()
@@ -0,0 +1,18 @@
import { computed } from 'vue'
/**
* This snippet comes from Thorsten Lünborg and is explained in this blog post https://www.vuemastery.com/blog/vue-3-data-down-events-up/
* @example const localValue = useModelWrapper(props, emit, 'modelValue')
*/
export function useModelWrapper<T, N extends string = 'modelValue'> (
props: T,
emit: (event: N, ...args: any[]) => void,
name = 'modelValue',
) {
return computed({
get: () => props[name],
set: (value: any) => {
emit(`update:${name}` as any, value)
},
})
}
@@ -1,4 +1,7 @@
import { createI18n as _createI18n } from 'vue-i18n'
import {
useI18n as _useI18n,
createI18n as _createI18n,
} from 'vue-i18n'
import type enUS from './en-US.json'
// Imports a special compiled messages object
@@ -20,3 +23,7 @@ export function createI18n (opts = {}) {
...opts,
})
}
export function useI18n () {
return _useI18n<{ message: MessageSchema }>({ useScope: 'global' })
}
+825
View File
@@ -0,0 +1,825 @@
/**
* Tailwind's Preflight Style Reset
* https://tailwindcss.com/docs/preflight
*
* Why is this here?
* 1. Tailwind doesn't publish their style reset (which is
* derived from modern-normalize).
* 2. WindiCSS's version of this doesn't work with third-party
* DOM elements that it can't extract.
*/
/*! tailwindcss v2.2.16 | MIT License | https://tailwindcss.com */
/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
@use 'modern-normalize/modern-normalize.css';
/*
Document
========
*/
/**
Use a better box model (opinionated).
*/
*,
::before,
::after {
box-sizing: border-box;
}
/**
Use a more readable tab size (opinionated).
*/
html {
-moz-tab-size: 4;
tab-size: 4;
}
/**
1. Correct the line height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/*
Sections
========
*/
/**
Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
*/
body {
font-family:
system-ui,
-apple-system, /* Firefox supports this but not yet `system-ui` */
'Segoe UI',
Roboto,
Helvetica,
Arial,
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji';
}
/*
Grouping content
================
*/
/**
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
*/
hr {
height: 0; /* 1 */
color: inherit; /* 2 */
}
/*
Text-level semantics
====================
*/
/**
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
}
/**
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
2. Correct the odd 'em' font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family:
ui-monospace,
SFMono-Regular,
Consolas,
'Liberation Mono',
Menlo,
monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
Tabular data
============
*/
/**
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
*/
table {
text-indent: 0; /* 1 */
border-color: inherit; /* 2 */
}
/*
Forms
=====
*/
/**
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
Remove the inheritance of text transform in Edge and Firefox.
1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button;
}
/**
Remove the inner border and padding in Firefox.
*/
::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
Restore the focus styles unset by the previous rule.
*/
:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
Remove the additional ':invalid' styles in Firefox.
See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737
*/
:-moz-ui-invalid {
box-shadow: none;
}
/**
Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
*/
legend {
padding: 0;
}
/**
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/**
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/**
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to 'inherit' in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/*
Interactive
===========
*/
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/**
* Manually forked from SUIT CSS Base: https://github.com/suitcss/base
* A thin layer on top of normalize.css that provides a starting point more
* suitable for web applications.
*/
/**
* Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
button {
background-color: transparent;
background-image: none;
}
fieldset {
margin: 0;
padding: 0;
}
ol,
ul {
list-style: none;
margin: 0;
padding: 0;
}
/**
* Tailwind custom reset styles
*/
/**
* 1. Use the user's configured `sans` font-family (with Tailwind's default
* sans-serif font stack as a fallback) as a sane default.
* 2. Use Tailwind's default "normal" line-height so the user isn't forced
* to override it to ensure consistency even when using the default theme.
*/
html {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 1 */
line-height: 1.5; /* 2 */
}
/**
* Inherit font-family and line-height from `html` so users can set them as
* a class directly on the `html` element.
*/
body {
font-family: inherit;
line-height: inherit;
}
/**
* 1. Prevent padding and border from affecting element width.
*
* We used to set this in the html element and inherit from
* the parent element for everything else. This caused issues
* in shadow-dom-enhanced elements like <details> where the content
* is wrapped by a div with box-sizing set to `content-box`.
*
* https://github.com/mozdevs/cssremedy/issues/4
*
*
* 2. Allow adding a border to an element by just adding a border-width.
*
* By default, the way the browser specifies that an element should have no
* border is by setting it's border-style to `none` in the user-agent
* stylesheet.
*
* In order to easily add borders to elements by just setting the `border-width`
* property, we change the default border-style for all elements to `solid`, and
* use border-width to hide them instead. This way our `border` utilities only
* need to set the `border-width` property instead of the entire `border`
* shorthand, making our border utilities much more straightforward to compose.
*
* https://github.com/tailwindcss/tailwindcss/pull/116
*/
*,
::before,
::after {
box-sizing: border-box; /* 1 */
border-width: 0; /* 2 */
border-style: solid; /* 2 */
border-color: currentColor; /* 2 */
}
/*
* Ensure horizontal rules are visible by default
*/
hr {
border-top-width: 1px;
}
/**
* Undo the `border-style: none` reset that Normalize applies to images so that
* our `border-{width}` utilities have the expected effect.
*
* The Normalize reset is unnecessary for us since we default the border-width
* to 0 on all elements.
*
* https://github.com/tailwindcss/tailwindcss/issues/362
*/
img {
border-style: solid;
}
textarea {
resize: vertical;
}
input::placeholder,
textarea::placeholder {
opacity: 1;
color: #9ca3af;
}
button,
[role="button"] {
cursor: pointer;
}
/**
* Override legacy focus reset from Normalize with modern Firefox focus styles.
*
* This is actually an improvement over the new defaults in Firefox in our testing,
* as it triggers the better focus styles even for links, which still use a dotted
* outline in Firefox by default.
*/
:-moz-focusring {
outline: auto;
}
table {
border-collapse: collapse;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/**
* Reset links to optimize for opt-in styling instead of
* opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/**
* Reset form element properties that are easy to forget to
* style explicitly so you don't inadvertently introduce
* styles that deviate from your design system. These styles
* supplement a partial reset that is already applied by
* normalize.css.
*/
button,
input,
optgroup,
select,
textarea {
padding: 0;
line-height: inherit;
color: inherit;
}
/**
* Use the configured 'mono' font family for elements that
* are expected to be rendered with a monospace font, falling
* back to the system monospace stack if there is no configured
* 'mono' font family.
*/
pre,
code,
kbd,
samp {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
/**
* 1. Make replaced elements `display: block` by default as that's
* the behavior you want almost all of the time. Inspired by
* CSS Remedy, with `svg` added as well.
*
* https://github.com/mozdevs/cssremedy/issues/14
*
* 2. Add `vertical-align: middle` to align replaced elements more
* sensibly by default when overriding `display` by adding a
* utility like `inline`.
*
* This can trigger a poorly considered linting error in some
* tools but is included by design.
*
* https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block; /* 1 */
vertical-align: middle; /* 2 */
}
/**
* Constrain images and videos to the parent width and preserve
* their intrinsic aspect ratio.
*
* https://github.com/mozdevs/cssremedy/issues/14
*/
img,
video {
max-width: 100%;
height: auto;
}
/**
* Ensure the default browser behavior of the `hidden` attribute.
*/
[hidden] {
display: none;
}
*, ::before, ::after {
--tw-border-opacity: 1;
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
}
/*
* Global Resets
* -------------------------
*/
// Input Resets
// Taken from Tailwind CSS Forms -- a plugin for tailwind.
// We could probably add these into WindiCSS directly
// https://github.com/tailwindlabs/tailwindcss-forms
[multiple],
[type=date],
[type=datetime-local],
[type=email],
[type=month],
[type=number],
[type=password],
[type=search],
[type=tel],
[type=text],
[type=time],
[type=url],
[type=week],
select,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
border-radius: 0;
padding-top: 0.35rem;
padding-bottom: 0.35rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
line-height: 1.25rem;
}
[multiple]:focus,
[type=date]:focus,
[type=datetime-local]:focus,
[type=email]:focus,
[type=month]:focus,
[type=number]:focus,
[type=password]:focus,
[type=search]:focus,
[type=tel]:focus,
[type=text]:focus,
[type=time]:focus,
[type=url]:focus,
[type=week]:focus,
select:focus,
textarea:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty, );
/*!*/
/*!*/
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
border-color: #2563eb;
}
input::-moz-placeholder,
textarea::-moz-placeholder {
color: #6b7280;
opacity: 1;
}
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #6b7280;
opacity: 1;
}
input::placeholder,
textarea::placeholder {
color: #6b7280;
opacity: 1;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-date-and-time-value {
min-height: 1.5em;
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right .5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
[multiple] {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: .75rem;
-webkit-print-color-adjust: unset;
color-adjust: unset;
}
[type=checkbox],
[type=radio] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
color: #2563eb;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
}
[type=checkbox] {
border-radius: 0;
}
[type=radio] {
border-radius: 100%;
}
[type=checkbox]:focus,
[type=radio]:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty);
--tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
}
[type=checkbox]:checked,
[type=radio]:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type=checkbox]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
[type=radio]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
}
[type=checkbox]:checked:focus,
[type=checkbox]:checked:hover,
[type=radio]:checked:focus,
[type=radio]:checked:hover {
border-color: transparent;
background-color: currentColor;
}
[type=checkbox]:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type=checkbox]:indeterminate:focus,
[type=checkbox]:indeterminate:hover {
border-color: transparent;
background-color: currentColor;
}
[type=file] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit;
}
[type=file]:focus {
outline: 1px auto -webkit-focus-ring-color;
}
input[type="search"].dark {
background: #222;
color: #fff;
}
input[type="search"].light {
background: #fff;
color: #222;
}
input[type="search"].suffix::-webkit-search-cancel-button {
@apply pr-3.25em;
}
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
margin: 1em;
height: 1em;
width: 1em;
border-radius: 50em;
background: url(https://pro.fontawesome.com/releases/v5.10.0/svgs/solid/times-circle.svg)
no-repeat 50% 50%;
background-size: contain;
opacity: 0;
pointer-events: none;
}
input[type="search"]:focus::-webkit-search-cancel-button {
opacity: 0.3;
pointer-events: all;
}
input[type="search"].dark::-webkit-search-cancel-button {
filter: invert(1);
}
@@ -0,0 +1,287 @@
@use './normalize.scss';
/* Define the "system" font family */
@font-face {
font-family: system;
font-style: normal;
font-weight: 300;
src: local(".SFNSText-Light"), local(".HelveticaNeueDeskInterface-Light"),
local(".LucidaGrandeUI"), local("Ubuntu Light"), local("Segoe UI Light"),
local("Roboto-Light"), local("DroidSans"), local("Tahoma");
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
em {
@apply text-purple-400 not-italic;
}
a {
@apply text-indigo-600 hover:underline;
}
/**
* Utilities
* -------------------------
*/
// Hides the scrollbar when overflow: auto. Use this sparingly -- it's not accessible!
.hide-scrollbar {
scrollbar-width: 0; // Firefox
// Chrome
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
/**
* Global Resets
* -------------------------
*/
// Input Resets
// Taken from Tailwind CSS Forms -- a plugin for tailwind.
// We could probably add these into WindiCSS directly
// https://github.com/tailwindlabs/tailwindcss-forms
[multiple],
[type=date],
[type=datetime-local],
[type=email],
[type=month],
[type=number],
[type=password],
[type=search],
[type=tel],
[type=text],
[type=time],
[type=url],
[type=week],
select,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
border-radius: 0;
padding-top: 0.35rem;
padding-bottom: 0.35rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
line-height: 1.25rem;
}
[multiple]:focus,
[type=date]:focus,
[type=datetime-local]:focus,
[type=email]:focus,
[type=month]:focus,
[type=number]:focus,
[type=password]:focus,
[type=search]:focus,
[type=tel]:focus,
[type=text]:focus,
[type=time]:focus,
[type=url]:focus,
[type=week]:focus,
select:focus,
textarea:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty, );
/*!*/
/*!*/
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
border-color: #2563eb;
}
input::-moz-placeholder,
textarea::-moz-placeholder {
color: #6b7280;
opacity: 1;
}
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #6b7280;
opacity: 1;
}
input::placeholder,
textarea::placeholder {
color: #6b7280;
opacity: 1;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-date-and-time-value {
min-height: 1.5em;
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right .5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
[multiple] {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: .75rem;
-webkit-print-color-adjust: unset;
color-adjust: unset;
}
[type=checkbox],
[type=radio] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
color: #2563eb;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
}
[type=checkbox] {
border-radius: 0;
}
[type=radio] {
border-radius: 100%;
}
[type=checkbox]:focus,
[type=radio]:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty);
--tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
}
[type=checkbox]:checked,
[type=radio]:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type=checkbox]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
[type=radio]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
}
[type=checkbox]:checked:focus,
[type=checkbox]:checked:hover,
[type=radio]:checked:focus,
[type=radio]:checked:hover {
border-color: transparent;
background-color: currentColor;
}
[type=checkbox]:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type=checkbox]:indeterminate:focus,
[type=checkbox]:indeterminate:hover {
border-color: transparent;
background-color: currentColor;
}
[type=file] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit;
}
[type=file]:focus {
outline: 1px auto -webkit-focus-ring-color;
}
input[type="search"].dark {
background: #222;
color: #fff;
}
input[type="search"].light {
background: #fff;
color: #222;
}
input[type="search"].suffix::-webkit-search-cancel-button {
@apply pr-3.25em;
}
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
margin: 1em;
height: 1em;
width: 1em;
border-radius: 50em;
background: url(https://pro.fontawesome.com/releases/v5.10.0/svgs/solid/times-circle.svg)
no-repeat 50% 50%;
background-size: contain;
opacity: 0;
pointer-events: none;
}
input[type="search"]:focus::-webkit-search-cancel-button {
opacity: 0.3;
pointer-events: all;
}
input[type="search"].dark::-webkit-search-cancel-button {
filter: invert(1);
}
+15 -2
View File
@@ -1,5 +1,4 @@
{
"include": ["src/**/*.vue", "src/**/*.tsx", "cypress/**/*.ts"],
"compilerOptions": {
/* Basic Options */
"target": "esnext",
@@ -48,7 +47,21 @@
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": ["../driver/src"], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [] /* List of folders to include type definitions from. */
"types": ["chrome", "./vue-shims", "./vite-env", "vite-plugin-icons", "@intlify/vite-plugin-vue-i18n/client", "@testing-library/cypress", "cypress-file-upload"], /* Type declaration files to be included in compilation. */
/* Type declaration files to be included in compilation. */
"paths": {
"@cy/i18n": ["../frontend-shared/src/locales/i18n"],
"@cy/components/*": ["../frontend-shared/src/components/*"]
},
"types": [
"chrome",
"./vue-shims",
"./vite-env",
"@intlify/vite-plugin-vue-i18n/client",
"@testing-library/cypress",
"cypress-file-upload",
"cypress"
],
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"noErrorTruncation": true,
+2
View File
@@ -1,2 +1,4 @@
/// <reference types="vite/client" />
/// <reference types="vite-plugin-pages/client" />
/// <reference types="vite-plugin-vue-layouts/client" />
/// <reference types="vite-svg-loader" />
+92
View File
@@ -0,0 +1,92 @@
import path from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import WindiCSS from 'vite-plugin-windicss'
import VueI18n from '@intlify/vite-plugin-vue-i18n'
import VueSvgLoader from 'vite-svg-loader'
import Components from 'unplugin-vue-components/vite'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
// eslint-disable-next-line no-duplicate-imports
import type { UserConfig } from 'vite'
import type { ArgumentsType } from '@antfu/utils'
type PluginOptions = {
plugins?: any[]
// These types aren't publicly exported
vueI18nOptions?: ArgumentsType<typeof VueI18n>[0]
iconsOptions?: ArgumentsType<typeof Icons>[0]
componentsOptions?: ArgumentsType<typeof Components>[0]
}
const base = './'
const alias = {
'@cy/components': path.resolve(__dirname, './src/components'),
// import { defaultMessages, useI18n } from '@cy/i18n'
'@cy/i18n': path.resolve(__dirname, './src/locales/i18n'),
}
const makePlugins = (plugins) => {
return ([
vue(),
vueJsx(),
VueI18n({
include: path.resolve(__dirname, './src/locales/**'),
...plugins.vueI18nOptions,
}),
Icons({
customCollections: {
// ~icons/cy/book-x16
cy: FileSystemIconLoader(path.resolve(__dirname, './src/assets/icons')),
...plugins.iconsOptions?.customCollections,
},
...plugins.iconsOptions,
}),
Components({
// <i-cy-book-x16/>
resolvers: IconsResolver({
customCollections: ['cy'],
}),
...plugins?.componentsOptions,
}),
WindiCSS(),
VueSvgLoader(),
// For new plugins only! Merge options for shared plugins via PluginOptions.
...(plugins?.plugins || []),
])
}
export const makeConfig = (config: Partial<UserConfig> = {}, plugins: PluginOptions = {}): UserConfig => {
// Don't overwrite plugins
delete config.plugins
return {
base,
// Production-only build options
build: {
minify: false,
},
resolve: { alias },
// You cannot add or remove arbitrary options from shared plugins.
// Please use the PluginsOverride option for this.
plugins: makePlugins(plugins),
define: {
'process.env': {
CYPRESS_INTERNAL_ENV: 'development',
},
'setImmediate': {},
},
...config,
}
}
export default defineConfig(makeConfig())
+27
View File
@@ -3,3 +3,30 @@ declare module 'virtual:*' {
const src: Component
export default src
}
declare module 'virtual:icons/*' {
// eslint-disable-next-line no-duplicate-imports
import { FunctionalComponent, SVGAttributes } from 'vue'
const component: FunctionalComponent<SVGAttributes>
export default component
}
declare module '~icons/*' {
// eslint-disable-next-line no-duplicate-imports
import { FunctionalComponent, SVGAttributes } from 'vue'
const component: FunctionalComponent<SVGAttributes>
export default component
}
declare module '~icons/cy/*' {
// eslint-disable-next-line no-duplicate-imports
import { FunctionalComponent, SVGAttributes } from 'vue'
const component: FunctionalComponent<SVGAttributes>
export default component
}
declare module '~icons/mdi/*' {
// eslint-disable-next-line no-duplicate-imports
import { FunctionalComponent, SVGAttributes } from 'vue'
const component: FunctionalComponent<SVGAttributes>
export default component
}
+34
View File
@@ -0,0 +1,34 @@
import { defineConfig } from 'windicss/helpers'
import InteractionVariants from '@windicss/plugin-interaction-variants'
import { IconDuotoneColorsPlugin } from './.windicss/icon-color-plugins'
import { safelist } from './.windicss/safelist'
import { colors } from './.windicss/colors'
export default defineConfig({
// This adds !important to all utility classes. https://csswizardry.com/2016/05/the-importance-of-important/
important: true,
theme: {
extend: {
gridTemplateColumns: {
launchpad: '64px 1fr',
},
gridTemplateRows: {
launchpad: '64px 1fr',
},
colors,
},
},
safelist,
variants: {
backgroundColor: ['group-focus-within', 'group-focus-visible', 'group-active', 'group-visited', 'group-disabled', 'hocus', 'group-hocus', 'can-hover', 'no-hover'],
},
plugins: [
InteractionVariants,
IconDuotoneColorsPlugin,
],
extract: {
// accepts globs and file paths relative to project root
include: ['index.html', '**/*.{vue,html,tsx}', '../frontend-shared/**/*.{vue,html,tsx}'],
exclude: ['node_modules/**/*', '.git/**/*'],
},
})
@@ -1,4 +1,3 @@
import { registerMountFn } from '@packages/frontend-shared/cypress/support/commands'
import { createI18n } from '@packages/launchpad/src/locales/i18n'
import { registerMountFn } from '@packages/frontend-shared/cypress/support/common'
registerMountFn({ plugins: [() => createI18n()] })
registerMountFn()
+1
View File
@@ -24,6 +24,7 @@
"@iconify/json": "1.1.368",
"@iconify/vue": "3.0.0-beta.1",
"@intlify/vite-plugin-vue-i18n": "2.4.0",
"@packages/frontend-shared": "0.0.0-development",
"@testing-library/cypress": "8.0.0",
"@urql/core": "2.3.1",
"@urql/devtools": "2.0.3",
@@ -1,4 +1,4 @@
import HeartIcon from 'virtual:vite-icons/mdi/heart'
import HeartIcon from '~icons/mdi/heart'
import InlineCodeEditor from './InlineCodeEditor.vue'
import { ref } from 'vue'
@@ -48,7 +48,11 @@ import _ from 'lodash'
import 'prismjs'
import '@packages/reporter/src/errors/prism.scss'
import CodeEditor from './CodeEditor.vue'
import { FunctionalComponent, SVGAttributes, computed, useSlots } from 'vue'
import Icon from '@cy/components/Icon.vue'
import type { FunctionalComponent, SVGAttributes } from 'vue'
// eslint-disable-next-line no-duplicate-imports
import { computed, useSlots } from 'vue'
import { useModelWrapper } from '../../composables'
const slots = useSlots()
@@ -1,6 +1,4 @@
import { computed } from 'vue'
import { useI18n as _useI18n } from 'vue-i18n'
import type { MessageSchema } from '../locales/schema'
/**
* This snippet comes from Thorsten Lünborg and is explained in this blog post https://www.vuemastery.com/blog/vue-3-data-down-events-up/
@@ -18,7 +16,3 @@ export function useModelWrapper<T, N extends string = 'modelValue'> (
},
})
}
export function useI18n () {
return _useI18n<{ message: MessageSchema }>({ useScope: 'global' })
}
@@ -1,4 +1,4 @@
import { defaultMessages } from '../locales/i18n'
import { defaultMessages } from '@cy/i18n'
import GlobalEmpty from './GlobalEmpty.vue'
const emptyText = defaultMessages.globalPage.empty
@@ -47,8 +47,8 @@
</template>
<script lang="ts" setup>
import { useI18n } from '../composables'
import IconPlaceholder from 'virtual:vite-icons/icons8/circle-thin'
import { useI18n } from '@cy/i18n'
import IconPlaceholder from '~icons/icons8/circle-thin'
import { FileSelector, Dropzone } from 'vue3-file-selector'
import { ref, watch, onMounted } from 'vue'
@@ -1,4 +1,4 @@
import { defaultMessages } from '../locales/i18n'
import { defaultMessages } from '@cy/i18n'
import GlobalPage from './GlobalPage.vue'
const searchSelector = `input[placeholder="${defaultMessages.globalPage.searchPlaceholder}"`
@@ -1,4 +1,4 @@
import { defaultMessages } from '../locales/i18n'
import { defaultMessages } from '@cy/i18n'
import GlobalPageHeader from './GlobalPageHeader.vue'
import { ref } from 'vue'
@@ -16,10 +16,11 @@
</template>
<script lang="ts" setup>
import Button from '../components/button/Button.vue'
import Input from '../components/input/Input.vue'
import IconPlus from 'virtual:vite-icons/mdi/plus'
import { useI18n, useModelWrapper } from '../composables'
import Button from '@cy/components/Button.vue'
import Input from '@cy/components/Input.vue'
import IconPlus from '~icons/mdi/plus'
import { useModelWrapper } from '../composables'
import { useI18n } from '@cy/i18n'
const { t } = useI18n()
@@ -25,10 +25,10 @@
<script setup lang="ts">
import { computed } from 'vue'
import Icon from '../components/icon/Icon.vue'
import IconChecked from 'virtual:vite-icons/mdi/check-circle'
import IconX from 'virtual:vite-icons/mdi/plus-circle'
import IconPending from 'virtual:vite-icons/mdi/refresh-circle'
import Icon from '@cy/components/Icon.vue'
import IconChecked from '~icons/mdi/check-circle'
import IconX from '~icons/mdi/plus-circle'
import IconPending from '~icons/mdi/refresh-circle'
import { getTimeAgo } from '../utils/time'
@@ -1,4 +1,4 @@
import { defaultMessages } from '../locales/i18n'
import { defaultMessages } from '@cy/i18n'
import WelcomeGuide from './WelcomeGuide.vue'
const welcomeGuide = defaultMessages.welcomeGuide.header
@@ -60,12 +60,12 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { useI18n } from '../composables'
import Checkbox from '../components/checkbox/Checkbox.vue'
import Button from '../components/button/Button.vue'
import { useI18n } from '@cy/i18n'
import Checkbox from '@cy/components/Checkbox.vue'
import Button from '@cy/components/Button.vue'
import WelcomeGuideLinks from './WelcomeGuideLinks.vue'
import IconCircleX from 'virtual:vite-icons/akar-icons/circle-x'
import IconPlaceholder from 'virtual:vite-icons/icons8/circle-thin'
import IconCircleX from '~icons/akar-icons/circle-x'
import IconPlaceholder from '~icons/icons8/circle-thin'
const projects = [
{ path: '~/Documents/GitHub/web' },
@@ -25,7 +25,7 @@
</template>
<script lang="ts" setup>
import Button from '../components/button/Button.vue'
import Button from '@cy/components/Button.vue'
const emit = defineEmits<{
(event: 'itemSelected', item: Record<string, string>): void
+4 -4
View File
@@ -68,10 +68,10 @@ import bottomBackground from '../images/bottom_filler.svg'
import { gql } from '@urql/core'
import { useMutation, useQuery } from '@urql/vue'
import { LayoutDocument, NavigationMenuSetItemDocument, NavItem } from '../generated/graphql'
import IconDashboardLine from 'virtual:vite-icons/clarity/dashboard-line'
import IconTerminalLine from 'virtual:vite-icons/clarity/terminal-line'
import IconSettingsLine from 'virtual:vite-icons/clarity/settings-line'
import IconRunsLine from 'virtual:vite-icons/clarity/bullet-list-line'
import IconDashboardLine from '~icons/clarity/dashboard-line'
import IconTerminalLine from '~icons/clarity/terminal-line'
import IconSettingsLine from '~icons/clarity/settings-line'
import IconRunsLine from '~icons/clarity/bullet-list-line'
gql`
query Layout {
@@ -1,5 +1,5 @@
import SideBarItem from './SideBarItem.vue'
import IconCoffee from 'virtual:vite-icons/mdi/coffee'
import IconCoffee from '~icons/mdi/coffee'
import { SideBarItemFragmentDoc } from '../generated/graphql-test'
describe('<SideBarItem />', { viewportWidth: 200, viewportHeight: 150 }, () => {
@@ -18,8 +18,12 @@
</template>
<script lang="ts" setup>
import Icon from '../components/icon/Icon.vue'
import { HTMLAttributes, FunctionalComponent, SVGAttributes, computed } from 'vue'
import Icon from '@cy/components/Icon.vue'
import { computed } from 'vue'
// eslint-disable-next-line no-duplicate-imports
import type { FunctionalComponent, SVGAttributes, HTMLAttributes } from 'vue'
import type { SideBarItemFragment } from '../generated/graphql'
import { gql } from '@urql/core'
-22
View File
@@ -1,22 +0,0 @@
import { createI18n as _createI18n } from 'vue-i18n'
import type enUS from './en-US.json'
// Imports a special compiled messages object
import compiledMessages from '@intlify/vite-plugin-vue-i18n/messages'
// The raw strings for the default language (en) used for testing
import rawJsonMessages from './en-US.json?raw'
export type MessageSchema = typeof enUS
export const defaultMessages: MessageSchema = JSON.parse(rawJsonMessages)
export const VueI18n = createI18n()
export function createI18n (opts = {}) {
return _createI18n<MessageSchema, 'en-US'>({
locale: 'en-US',
messages: compiledMessages,
...opts,
})
}
-8
View File
@@ -1,8 +0,0 @@
/**
* define the resource schema
*/
import type enUS from './en-US.json'
// define the locale message schema as master
export type MessageSchema = typeof enUS
+1 -285
View File
@@ -1,285 +1 @@
/* Define the "system" font family */
@font-face {
font-family: system;
font-style: normal;
font-weight: 300;
src: local(".SFNSText-Light"), local(".HelveticaNeueDeskInterface-Light"),
local(".LucidaGrandeUI"), local("Ubuntu Light"), local("Segoe UI Light"),
local("Roboto-Light"), local("DroidSans"), local("Tahoma");
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
em {
@apply text-purple-400 not-italic;
}
a {
@apply text-indigo-600 hover:underline;
}
/**
* Utilities
* -------------------------
*/
// Hides the scrollbar when overflow: auto. Use this sparingly -- it's not accessible!
.hide-scrollbar {
scrollbar-width: 0; // Firefox
// Chrome
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
/**
* Global Resets
* -------------------------
*/
// Input Resets
// Taken from Tailwind CSS Forms -- a plugin for tailwind.
// We could probably add these into WindiCSS directly
// https://github.com/tailwindlabs/tailwindcss-forms
[multiple],
[type=date],
[type=datetime-local],
[type=email],
[type=month],
[type=number],
[type=password],
[type=search],
[type=tel],
[type=text],
[type=time],
[type=url],
[type=week],
select,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
border-radius: 0;
padding-top: 0.35rem;
padding-bottom: 0.35rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
font-size: 1rem;
line-height: 1.25rem;
}
[multiple]:focus,
[type=date]:focus,
[type=datetime-local]:focus,
[type=email]:focus,
[type=month]:focus,
[type=number]:focus,
[type=password]:focus,
[type=search]:focus,
[type=tel]:focus,
[type=text]:focus,
[type=time]:focus,
[type=url]:focus,
[type=week]:focus,
select:focus,
textarea:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty, );
/*!*/
/*!*/
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
border-color: #2563eb;
}
input::-moz-placeholder,
textarea::-moz-placeholder {
color: #6b7280;
opacity: 1;
}
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #6b7280;
opacity: 1;
}
input::placeholder,
textarea::placeholder {
color: #6b7280;
opacity: 1;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-date-and-time-value {
min-height: 1.5em;
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right .5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
[multiple] {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: .75rem;
-webkit-print-color-adjust: unset;
color-adjust: unset;
}
[type=checkbox],
[type=radio] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
color: #2563eb;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
}
[type=checkbox] {
border-radius: 0;
}
[type=radio] {
border-radius: 100%;
}
[type=checkbox]:focus,
[type=radio]:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty);
--tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
}
[type=checkbox]:checked,
[type=radio]:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type=checkbox]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
[type=radio]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
}
[type=checkbox]:checked:focus,
[type=checkbox]:checked:hover,
[type=radio]:checked:focus,
[type=radio]:checked:hover {
border-color: transparent;
background-color: currentColor;
}
[type=checkbox]:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type=checkbox]:indeterminate:focus,
[type=checkbox]:indeterminate:hover {
border-color: transparent;
background-color: currentColor;
}
[type=file] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit;
}
[type=file]:focus {
outline: 1px auto -webkit-focus-ring-color;
}
input[type="search"].dark {
background: #222;
color: #fff;
}
input[type="search"].light {
background: #fff;
color: #222;
}
input[type="search"].suffix::-webkit-search-cancel-button {
@apply pr-3.25em;
}
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
margin: 1em;
height: 1em;
width: 1em;
border-radius: 50em;
background: url(https://pro.fontawesome.com/releases/v5.10.0/svgs/solid/times-circle.svg)
no-repeat 50% 50%;
background-size: contain;
opacity: 0;
pointer-events: none;
}
input[type="search"]:focus::-webkit-search-cancel-button {
opacity: 0.3;
pointer-events: all;
}
input[type="search"].dark::-webkit-search-cancel-button {
filter: invert(1);
}
@use '@packages/frontend-shared/src/styles/shared.scss';
+1 -1
View File
@@ -4,7 +4,7 @@ import 'virtual:windi.css'
import urql from '@urql/vue'
import App from './App.vue'
import { makeUrqlClient } from '@packages/frontend-shared/src/graphql/urqlClient'
import { createI18n } from './locales/i18n'
import { createI18n } from '@cy/i18n'
const app = createApp(App)
+2 -2
View File
@@ -38,9 +38,9 @@
import RunIcon from './RunIcon.vue'
import RunResults from './RunResults.vue'
// bx:bx-user-circle
import IconUserCircle from 'virtual:vite-icons/bx/bx-user-circle'
import IconUserCircle from '~icons/bx/bx-user-circle'
// carbon:branch
import IconBranch from 'virtual:vite-icons/carbon/branch'
import IconBranch from '~icons/carbon/branch'
import { gql } from '@urql/core'
import type { RunCardFragment } from '../generated/graphql'
import { computed } from 'vue-demi'
+4 -4
View File
@@ -27,13 +27,13 @@
import type { RunResultsFragment } from '../generated/graphql'
import { gql } from '@urql/core'
// bi:check-lg
import IconPass from 'virtual:vite-icons/bi/check-lg'
import IconPass from '~icons/bi/check-lg'
// eva:close-fill
import IconFail from 'virtual:vite-icons/eva/close-fill'
import IconFail from '~icons/eva/close-fill'
// fa-solid:snowflake
import IconFlake from 'virtual:vite-icons/fa-solid/snowflake'
import IconFlake from '~icons/fa-solid/snowflake'
// line-md:cancel
import IconCancel from 'virtual:vite-icons/line-md/cancel'
import IconCancel from '~icons/line-md/cancel'
gql`
fragment RunResults on CloudRun {
@@ -1,5 +1,5 @@
import SettingsCard from './SettingsCard.vue'
import IconLaptop from 'virtual:vite-icons/mdi/laptop'
import IconLaptop from '~icons/mdi/laptop'
describe('<SettingsCard />', () => {
it('renders', () => {
@@ -46,8 +46,9 @@
</template>
<script lang="ts" setup>
import IconCaret from 'virtual:vite-icons/mdi/caret'
import Icon from '../components/icon/Icon.vue'
import IconCaret from '~icons/mdi/caret'
import Icon from '@cy/components/Icon.vue'
// eslint-disable-next-line no-duplicate-imports
import type { FunctionalComponent, SVGAttributes } from 'vue'
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
@@ -18,12 +18,12 @@
</template>
<script lang="ts" setup>
import { useI18n } from '../composables'
import { useI18n } from '@cy/i18n'
import SettingsCard from './SettingsCard.vue'
import ProjectSettings from './project/ProjectSettings.vue'
import DeviceSettings from './device/DeviceSettings.vue'
import IconLaptop from 'virtual:vite-icons/mdi/laptop'
import IconBook from 'virtual:vite-icons/mdi/book'
import IconLaptop from '~icons/mdi/laptop'
import IconBook from '~icons/mdi/book'
const { t } = useI18n()
</script>
@@ -1,5 +1,5 @@
import ExternalEditorSettings from './ExternalEditorSettings.vue'
import { defaultMessages } from '../../locales/i18n'
import { defaultMessages } from '@cy/i18n'
const editorText = defaultMessages.settingsPage.editor
@@ -38,17 +38,17 @@
<script lang="ts" setup>
import { ref } from 'vue'
import Icon from '../../components/icon/Icon.vue'
import Icon from '@cy/components/Icon.vue'
import SettingsSection from '../SettingsSection.vue'
import { useI18n } from '../../composables'
import Select from '../../components/input/Select.vue'
import VSCode from 'virtual:vite-icons/logos/visual-studio-code'
import Atom from 'virtual:vite-icons/logos/atom-icon'
import Webstorm from 'virtual:vite-icons/logos/webstorm'
import Vim from 'virtual:vite-icons/logos/vim'
import Sublime from 'virtual:vite-icons/logos/sublimetext-icon'
import Emacs from 'virtual:vite-icons/logos/emacs'
import IconTerminal from 'virtual:vite-icons/mdi/terminal'
import { useI18n } from '@cy/i18n'
import Select from '@cy/components/Select.vue'
import VSCode from '~icons/logos/visual-studio-code'
import Atom from '~icons/logos/atom-icon'
import Webstorm from '~icons/logos/webstorm'
import Vim from '~icons/logos/vim'
import Sublime from '~icons/logos/sublimetext-icon'
import Emacs from '~icons/logos/emacs'
import IconTerminal from '~icons/mdi/terminal'
// TODO, grab these from gql or the user's machine.
const externalEditors = [
@@ -27,7 +27,7 @@
<script lang="ts" setup>
import SettingsSection from '../SettingsSection.vue'
import { useI18n } from '../../composables'
import { useI18n } from '@cy/i18n'
import { ref } from 'vue'
const { t } = useI18n()
@@ -1,6 +1,6 @@
import Config from './Config.vue'
import jsonObject from '../../../cypress.json?raw'
import { defaultMessages } from '../../locales/i18n'
import { defaultMessages } from '@cy/i18n'
import { each } from 'lodash'
describe('<Config/>', () => {

Some files were not shown because too many files have changed in this diff Show More