From 73635e5a327667a62f08cd4e7e77ed73b446cedd Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 9 May 2018 14:20:45 -0700 Subject: [PATCH 1/4] add pagination component --- modules/primer-pagination/LICENSE | 21 +++++ modules/primer-pagination/README.md | 86 +++++++++++++++++++ modules/primer-pagination/index.scss | 3 + modules/primer-pagination/lib/pagination.scss | 74 ++++++++++++++++ modules/primer-pagination/package.json | 33 +++++++ modules/primer-pagination/stories.js | 10 +++ 6 files changed, 227 insertions(+) create mode 100644 modules/primer-pagination/LICENSE create mode 100644 modules/primer-pagination/README.md create mode 100644 modules/primer-pagination/index.scss create mode 100644 modules/primer-pagination/lib/pagination.scss create mode 100644 modules/primer-pagination/package.json create mode 100644 modules/primer-pagination/stories.js diff --git a/modules/primer-pagination/LICENSE b/modules/primer-pagination/LICENSE new file mode 100644 index 0000000000..71fbac5e24 --- /dev/null +++ b/modules/primer-pagination/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/modules/primer-pagination/README.md b/modules/primer-pagination/README.md new file mode 100644 index 0000000000..854d0ff692 --- /dev/null +++ b/modules/primer-pagination/README.md @@ -0,0 +1,86 @@ +# Primer Pagination + +[![npm version](https://img.shields.io/npm/v/primer-pagination.svg)](https://www.npmjs.org/package/primer-pagination) +[![Build Status](https://travis-ci.org/primer/primer.svg?branch=master)](https://travis-ci.org/primer/primer) + +> Pagination component for applying button styles to a connected set of links that go to related pages + +This repository is a module of the full [primer][primer] repository. + +## Install + +This repository is distributed with [npm]. After [installing npm][install-npm], you can install `primer-pagination` with this command. + +``` +$ npm install --save primer-pagination +``` + +## Usage + +The source files included are written in [Sass][sass] (SCSS) You can simply point your sass `include-path` at your `node_modules` directory and import it like this. + +```scss +@import "primer-pagination/index.scss"; +``` + +You can also import specific portions of the module by importing those partials from the `/lib/` folder. _Make sure you import any requirements along with the modules._ + +## Build + +For a compiled **CSS** version of this module, an npm script is included that will output a css version to `build/build.css` The built css file is also included in the npm package: + +``` +$ npm run build +``` + +## Documentation + + + +Use the pagination component to apply button styles to a connected set of links that go to related pages (for example, previous, next, or page numbers). + +{:toc} + +## Previous/next pagination + +You can make a very simple pagination container with just the Previous and Next buttons. Add the class `disabled` to the `Previous` button if there isn't a preceding page, or to the `Next` button if there isn't a succeeding page. + +```html + +``` + +## Numbered pagination + +For pagination across multiple pages, make sure it's clear to the user where they are in a set of pages. + +To do this, add anchor links to the `pagination` element. Previous and Next buttons should always be present. Add the class `disabled` to the Previous button if you're on the first page. Apply the class `current` to the current numbered page. + +As always, make sure to include the appropriate `aria` attributes to make the element accessible. + +- Add `aria-label="Pagination"` to the the `paginate-container` element. +- Add `aria-current="true"` to the current page marker. +- Add `aria-label="Page X"` to each anchor link. + +```html + +``` diff --git a/modules/primer-pagination/index.scss b/modules/primer-pagination/index.scss new file mode 100644 index 0000000000..32c9cd819d --- /dev/null +++ b/modules/primer-pagination/index.scss @@ -0,0 +1,3 @@ +// support files +@import "primer-support/index.scss"; +@import "./lib/pagination.scss"; diff --git a/modules/primer-pagination/lib/pagination.scss b/modules/primer-pagination/lib/pagination.scss new file mode 100644 index 0000000000..ad92649ecf --- /dev/null +++ b/modules/primer-pagination/lib/pagination.scss @@ -0,0 +1,74 @@ +// Needs refactoring +// stylelint-disable selector-max-type +.pagination { + @include clearfix; + + a, + span, + em { + position: relative; + float: left; + padding: 7px 12px; + margin-left: -1px; + font-size: 13px; + font-style: normal; + font-weight: $font-weight-bold; + color: $text-blue; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + user-select: none; + background: $bg-white; // Reset default gradient backgrounds and colors + border: 1px solid $gray-200; + + &:first-child { + margin-left: 0; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + } + + &:last-child { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + } + + // Bring any button into forefront for proper borders given negative margin below + &:hover, + &:focus { + z-index: 2; + text-decoration: none; + background-color: darken($gray-100, 2%); + border-color: $gray-200; + } + } + + .selected { z-index: 3; } + + .current, + .current:hover { + z-index: 3; + color: $text-white; + background-color: $bg-blue; + border-color: $blue; + } + + .gap, + .disabled, + .gap:hover, + .disabled:hover { + color: $gray-300; + cursor: default; + background-color: $gray-000; + } +} + +// Unified centered pagination across the site +.paginate-container { + margin-top: 20px; + margin-bottom: 15px; + text-align: center; + + .pagination { + display: inline-block; + } +} diff --git a/modules/primer-pagination/package.json b/modules/primer-pagination/package.json new file mode 100644 index 0000000000..75a0dbf38c --- /dev/null +++ b/modules/primer-pagination/package.json @@ -0,0 +1,33 @@ +{ + "version": "0.0.1", + "name": "primer-pagination", + "description": "Pagination component for applying button styles to a connected set of links that go to related pages", + "homepage": "http://primer.github.io/", + "primer": { + "category": "product", + "module_type": "components" + }, + "author": "GitHub, Inc.", + "license": "MIT", + "style": "index.scss", + "sass": "index.scss", + "main": "build/index.js", + "repository": "https://github.com/primer/primer/tree/master/modules/primer-pagination", + "bugs": { + "url": "https://github.com/primer/primer/issues" + }, + "scripts": { + "test-docs": "../../script/test-docs", + "build": "../../script/npm-run primer-module-build index.scss", + "prepare": "npm run build", + "lint": "../../script/lint-scss", + "test": "../../script/npm-run-all build lint test-docs" + }, + "dependencies": { + "primer-support": "4.5.2" + }, + "keywords": [ + "github", + "primer" + ] +} diff --git a/modules/primer-pagination/stories.js b/modules/primer-pagination/stories.js new file mode 100644 index 0000000000..381896e16f --- /dev/null +++ b/modules/primer-pagination/stories.js @@ -0,0 +1,10 @@ +import React from 'react' +import { storiesOf } from '@storybook/react' +import storiesFromMarkdown from '../../.storybook/lib/storiesFromMarkdown' + +const stories = storiesOf('Pagination', module) + +storiesFromMarkdown(require.context('.', true, /\.md$/)) + .forEach(({title, story}) => { + stories.add(title, story) + }) From 6511252a0557b10c399ad19a6096b748ec776127 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 9 May 2018 14:28:29 -0700 Subject: [PATCH 2/4] replace border radius values with variable --- modules/primer-pagination/lib/pagination.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/primer-pagination/lib/pagination.scss b/modules/primer-pagination/lib/pagination.scss index ad92649ecf..ff81a5e4f5 100644 --- a/modules/primer-pagination/lib/pagination.scss +++ b/modules/primer-pagination/lib/pagination.scss @@ -23,13 +23,13 @@ &:first-child { margin-left: 0; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; + border-top-left-radius: $border-radius; + border-bottom-left-radius: $border-radius; } &:last-child { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; + border-top-right-radius: $border-radius; + border-bottom-right-radius: $border-radius; } // Bring any button into forefront for proper borders given negative margin below From 04f0ec5f23aaf3e20c547a5356d561b60b77d19a Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 9 May 2018 14:48:51 -0700 Subject: [PATCH 3/4] add pagination to primer-core index.scss --- modules/primer-core/index.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/primer-core/index.scss b/modules/primer-core/index.scss index 339fa21e1e..96252e00ff 100644 --- a/modules/primer-core/index.scss +++ b/modules/primer-core/index.scss @@ -22,6 +22,7 @@ @import "primer-forms/index.scss"; @import "primer-layout/index.scss"; @import "primer-navigation/index.scss"; +@import "primer-pagination/index.scss"; @import "primer-tooltips/index.scss"; @import "primer-truncate/index.scss"; From 1b98b724e96d171722d21c6ce1539ee8174aa501 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 9 May 2018 15:22:51 -0700 Subject: [PATCH 4/4] Revert "Merge branch 'dev' into task/add-pagination-component" This reverts commit 710cc7c41d0d908614ed239b9a90d588b5891b1a, reversing changes made to 6511252a0557b10c399ad19a6096b748ec776127. --- .storybook/lib/storiesFromMarkdown.js | 42 ++-- .storybook/webpack.config.js | 32 ++- README.md | 2 +- lerna.json | 1 - meta/scoreboard/index.js | 96 --------- meta/scoreboard/package.json | 13 -- modules/primer-alerts/lib/flash.scss | 1 - modules/primer-alerts/package.json | 5 +- modules/primer-base/package.json | 6 +- .../primer-marketing-utilities/package.json | 6 +- modules/primer-popover/README.md | 183 +++++------------- modules/primer-popover/lib/popover.scss | 6 +- modules/primer-support/docs/spacing.md | 12 +- package.json | 13 +- script/lint-scss | 2 +- script/npm-run-all | 2 +- script/test-docs | 2 +- tests/modules/test-document-styles.js | 116 +++++------ tools/primer-module-build/.eslintrc.json | 14 -- tools/primer-module-build/LICENSE | 21 -- tools/primer-module-build/cli.js | 29 --- tools/primer-module-build/index.js | 19 -- tools/primer-module-build/lib/.postcss.json | 6 - tools/primer-module-build/lib/build.js | 69 ------- tools/primer-module-build/package.json | 49 ----- .../tests/fixtures/primer-package.scss | 1 - .../tests/fixtures/relative.scss | 1 - tools/primer-module-build/tests/index.js | 33 ---- 28 files changed, 149 insertions(+), 633 deletions(-) delete mode 100644 meta/scoreboard/index.js delete mode 100644 meta/scoreboard/package.json delete mode 100644 tools/primer-module-build/.eslintrc.json delete mode 100644 tools/primer-module-build/LICENSE delete mode 100755 tools/primer-module-build/cli.js delete mode 100644 tools/primer-module-build/index.js delete mode 100644 tools/primer-module-build/lib/.postcss.json delete mode 100644 tools/primer-module-build/lib/build.js delete mode 100644 tools/primer-module-build/package.json delete mode 100644 tools/primer-module-build/tests/fixtures/primer-package.scss delete mode 100644 tools/primer-module-build/tests/fixtures/relative.scss delete mode 100644 tools/primer-module-build/tests/index.js diff --git a/.storybook/lib/storiesFromMarkdown.js b/.storybook/lib/storiesFromMarkdown.js index 081d42feca..564600dfdb 100644 --- a/.storybook/lib/storiesFromMarkdown.js +++ b/.storybook/lib/storiesFromMarkdown.js @@ -19,22 +19,17 @@ const railsOcticonToReact = (html) => { return html } -const parseBlockAttrs = (node, file) => { - const pairs = node.lang.replace(/^html\s*/, '') - const attrs = pairs.length ? parsePairs(pairs) : {} - attrs.title = attrs.title - || getPreviousHeading(node) - || `story @ ${file}:${node.position.start.line}` - node.block = attrs - return node -} - const nodeToStory = (node, file) => { const html = railsOcticonToReact(node.value) - const {title} = node.block + const element = htmlParser.parse(html) + const pairs = node.lang.replace(/^html\s*/, '') + const attrs = pairs.length ? parsePairs(pairs) : {} + const title = attrs.title || getPreviousHeading(node) || + `story @ ${file}:${node.position.start.line}` return { title, - story: () => htmlParser.parse(html), + story: () => element, + attrs, html, file, node, @@ -49,17 +44,14 @@ const getPreviousHeading = node => { } export default req => { - return req.keys() - .filter(file => !file.match(/node_modules/)) - .reduce((stories, file) => { - const content = req(file) - const ast = parents(remark.parse(content)) - const path = file.replace(/^\.\//, '') - return stories.concat( - select(ast, 'code[lang^=html]') - .map(parseBlockAttrs) - .filter(({block}) => block.story !== "false") - .map(node => nodeToStory(node, path)) - ) - }, []) + return req.keys().reduce((stories, file) => { + const content = req(file) + const ast = parents(remark.parse(content)) + const path = file.replace(/^\.\//, '') + return stories.concat( + select(ast, 'code[lang^=html]') + .map(node => nodeToStory(node, path)) + .filter(({attrs}) => attrs.story !== "false") + ) + }, []) } diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index be4eb6596b..59c421d1d3 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -1,6 +1,6 @@ -const path = require('path'); +const path = require("path"); -const modulesPath = path.resolve(__dirname, '../modules') +const modulesPath = path.resolve(__dirname, "../modules") module.exports = (config, env) => { @@ -9,34 +9,26 @@ module.exports = (config, env) => { .filter(plugin => plugin.constructor.name !== 'UglifyJsPlugin') } - const rules = config.module.rules - - rules.forEach((rule, index) => { - if ('README.md'.match(rule.test)) { - // console.warn('replacing MD rule:', rule) - rules.splice(index, 1, { - test: /\.md$/, - loader: 'raw-loader', - }) - } - }) - - rules.push( + config.module.rules.push( + { + test: /\.md$/, + use: "raw-loader", + }, { test: /\.scss$/, loaders: [ - 'style-loader', - 'css-loader', + "style-loader", + "css-loader", { - loader: 'postcss-loader', + loader: "postcss-loader", options: { config: { - path: require.resolve('./postcss.config.js'), + path: require.resolve("./postcss.config.js"), }, }, }, { - loader: 'sass-loader', + loader: "sass-loader", options: { includePaths: [ modulesPath, diff --git a/README.md b/README.md index b3793fe480..5b07f7be6d 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Then, you would import the module with: @import "primer-navigation/index.scss"; ``` -Or, while you're figuring out which modules you need, you can import them directly from the `primer` [`modules` directory](./modules) like so: +Or, while you're figuring out which modules you need, you can import them directly from the `primer` [`packages` directory](./packages) like so: ```scss @import "primer/modules/primer-navigation/index.css"; diff --git a/lerna.json b/lerna.json index 9af25e395a..c0e596e607 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,6 @@ { "lerna": "2.4.0", "packages": [ - "meta/*", "modules/*", "tools/*" ], diff --git a/meta/scoreboard/index.js b/meta/scoreboard/index.js deleted file mode 100644 index 924c49753f..0000000000 --- a/meta/scoreboard/index.js +++ /dev/null @@ -1,96 +0,0 @@ -const {basename, join, resolve} = require('path') -const PromiseQueue = require('p-queue') -const execa = require('execa') -const globby = require('globby') -const rootDir = resolve(__dirname, '../..') -const lernaConfig = require(join(rootDir, 'lerna.json')) -const modulesDir = join(rootDir, 'modules') -require('console.table') - -const unique = list => Array.from(new Set(list)).sort() - -const matchAll = (pattern, text) => { - const matches = [] - let match - while (match = pattern.exec(text)) { - matches.push(match) - } - return matches -} - -const checks = { - 'has stories': (module, key) => { - return globby(join(module.path, '**/stories.js')) - .then(files => ({ - [key]: files.length > 0 ? 'yes' : 'no' - })) - }, - 'docs test': (module, key) => { - return execa(join(rootDir, 'script/test-docs'), { - cwd: module.path - }) - .then(result => ({[key]: 'pass'})) - .catch(({stderr}) => { - const pattern = /("\.[-\w]+") is not documented/g - const matches = matchAll(pattern, stderr) - .map(match => match[1]) - let missing = matches ? Array.from(matches) : [] - const max = 5 - if (missing.length > max) { - const more = missing.length - max - missing = missing.slice(0, max).concat(`and ${more} more...`) - } - return { - [key]: 'FAIL', - 'missing docs': unique(missing).join(', ') - } - }) - } -} - -const args = process.argv.slice(2) - -const modules = args.length - ? Promise.resolve(args) - : globby(join(modulesDir, 'primer-*')) - -modules - .then(moduleDirs => { - console.log('Found %d module directories', moduleDirs.length) - return moduleDirs - .map(path => ({ - path, - name: basename(path), - pkg: require(join(path, 'package.json')) - })) - .filter(({pkg}) => pkg.primer.module_type !== 'meta') - }) - .then(modules => { - console.log('Filtered to %d modules (excluding meta-packages)', modules.length) - - const queue = new PromiseQueue({concurrency: 3}) - - for (const module of modules) { - module.checks = {} - for (const [name, check] of Object.entries(checks)) { - queue.add(() => { - // console.warn(`? check: ${module.name} ${name}`) - return check(module, name) - .then(result => { - Object.assign(module.checks, result) - }) - }) - } - } - - console.warn(`Running ${queue.size} checks...`) - return queue.onIdle().then(() => modules) - }) - .then(modules => { - console.warn('ran tests on %d modules', modules.length) - const rows = modules.map(({name, checks}) => { - return Object.assign({'package': name}, checks) - }) - console.table(rows) - }) - diff --git a/meta/scoreboard/package.json b/meta/scoreboard/package.json deleted file mode 100644 index 6563d2587b..0000000000 --- a/meta/scoreboard/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "private": true, - "name": "primer-scorecard", - "scripts": { - "test": "node index.js" - }, - "devDependencies": { - "console.table": "^0.10.0", - "execa": "^0.10.0", - "globby": "^6.1.0", - "p-queue": "^2.4.2" - } -} diff --git a/modules/primer-alerts/lib/flash.scss b/modules/primer-alerts/lib/flash.scss index 23edbcdbd5..029c7fe291 100644 --- a/modules/primer-alerts/lib/flash.scss +++ b/modules/primer-alerts/lib/flash.scss @@ -72,7 +72,6 @@ border-radius: 0; } -// FIXME deprecate this .warning { padding: $em-spacer-5; margin-bottom: 0.8em; diff --git a/modules/primer-alerts/package.json b/modules/primer-alerts/package.json index 634684e899..7f0335e4ca 100644 --- a/modules/primer-alerts/package.json +++ b/modules/primer-alerts/package.json @@ -10,10 +10,7 @@ "main": "build/index.js", "primer": { "category": "product", - "module_type": "components", - "class_whitelist": [ - "warning" - ] + "module_type": "components" }, "files": [ "index.scss", diff --git a/modules/primer-base/package.json b/modules/primer-base/package.json index 7966845722..6da36b07ac 100644 --- a/modules/primer-base/package.json +++ b/modules/primer-base/package.json @@ -10,11 +10,7 @@ "main": "build/index.js", "primer": { "category": "core", - "module_type": "support", - "class_whitelist": [ - "octicon", - "rule" - ] + "module_type": "support" }, "files": [ "index.scss", diff --git a/modules/primer-marketing-utilities/package.json b/modules/primer-marketing-utilities/package.json index dcc2329ba6..6f5a76a498 100644 --- a/modules/primer-marketing-utilities/package.json +++ b/modules/primer-marketing-utilities/package.json @@ -10,11 +10,7 @@ "main": "build/index.js", "primer": { "category": "marketing", - "module_type": "utilities", - "class_whitelist": [ - "border-??-*", - "position-??-*" - ] + "module_type": "utilities" }, "files": [ "index.scss", diff --git a/modules/primer-popover/README.md b/modules/primer-popover/README.md index 113e4f80b1..a32d5b8570 100644 --- a/modules/primer-popover/README.md +++ b/modules/primer-popover/README.md @@ -44,7 +44,7 @@ Popovers are used to bring attention to specific user interface elements, typica {:toc} -A popover consist of: +Popover's consist of: - The block element, `.Popover`, which simply positions its content absolutely atop other body content. - The child element, `.Popover-message`, which contains the markup for the intended messaging and the visual "caret." @@ -53,23 +53,19 @@ In the examples below, `Popover-message`, in particular, uses a handful of utili The `Popover-message` element also supports several modifiers, most of which position the caret differently: -- [`.Popover-message--top`](#default-top-center) (default): Places the caret on the top edge of the message, horizontally centered. -- [`.Popover-message--bottom`](#bottom) Places the caret on the bottom edge of the message, horizontally centered. -- [`.Popover-message--right`](#right): Places the caret on the right edge of the message, vertically centered. -- [`.Popover-message--left`](#left): Places the caret on the left edge of the message, vertically centered. +- `.Popover-message--top` (default): Places the caret on the top edge of the message, horizontally centered. +- `.Popover-message--bottom`: Places the caret on the bottom edge of the message, horizontally centered. +- `.Popover-message--right`: Places the caret on the right edge of the message, vertically centered. +- `.Popover-message--left`: Places the caret on the left edge of the message, vertically centered. Each of these modifiers also support a syntax for adjusting the positioning the caret to the right, left, top, or bottom of its respective edge. That syntax looks like: -- [`.Popover-message--bottom-left`](#bottom-left) -- [`.Popover-message--bottom-right`](#bottom-right) -- [`.Popover-message--left-bottom`](#left-bottom) -- [`.Popover-message--left-top`](#left-top) -- [`.Popover-message--right-bottom`](#right-bottom) -- [`.Popover-message--right-top`](#right-top) -- [`.Popover-message--top-left`](#top-left) -- [`.Popover-message--top-right`](#top-right) +- `.Popover-message--top-right` +- `.Popover-message--right-top` +- `.Popover-message--bottom-left` +- `.Popover-message--left-bottom` -Lastly, there is an added [`.Popover-message--large`](#large) modifier, which assumes a slightly wider popover message on screens wider than 544px. +Lastly, there is an added `.Popover-message--large` modifier, which assumes a slightly wider popover message on screens wider than 544px. ### Notes @@ -109,71 +105,13 @@ Defaults to caret oriented top-center. ``` -### Bottom +### Top-right-aligned example -```html title="Bottom" -
-
-
-

Popover heading

-

Message about this particular piece of UI.

- -
-
- -
-``` - -### Bottom-right - -```html title="Bottom-right" +```html title="Top-right"
-
-
-

Popover heading

-

Message about this particular piece of UI.

- -
-
-
-``` - -### Bottom-left - -```html title="Bottom-left" -
-
-

Popover heading

-

Message about this particular piece of UI.

- -
-
- -``` - -### Left - -```html title="Left" -
- -
-
-

Popover heading

-

Message about this particular piece of UI.

- -
-
-
-``` - -### Left-bottom - -```html title="Left-bottom" -
- -
-
+
+

Popover heading

Message about this particular piece of UI.

@@ -182,13 +120,13 @@ Defaults to caret oriented top-center.
``` -### Left-top +### Top-right-aligned example -```html title="Left-top" -
+```html title="Top-left" +
-
-
+
+

Popover heading

Message about this particular piece of UI.

@@ -197,77 +135,62 @@ Defaults to caret oriented top-center.
``` -### Right +### Right-aligned example ```html title="Right" -
-
-
-

Popover heading

-

Message about this particular piece of UI.

- -
+
+
+

Popover heading

+

Message about this particular piece of UI.

+
-
``` -### Right-bottom +### Left-aligned example -```html title="Right-bottom" -
-
-
-

Popover heading

-

Message about this particular piece of UI.

- -
+```html title="Left" +
+
+

Popover heading

+

Message about this particular piece of UI.

+
-
``` -### Right-top +### Bottom-aligned example -```html title="Right-top" -
-
-
-

Popover heading

-

Message about this particular piece of UI.

- -
+```html title="Bottom" +
+
+

Popover heading

+

Message about this particular piece of UI.

+
-
``` -### Top-left +### Bottom-right-aligned example -```html title="Top-left" -
- -
-
-

Popover heading

-

Message about this particular piece of UI.

- -
+```html title="Bottom-right" +
+
+

Popover heading

+

Message about this particular piece of UI.

+
``` -### Top-right +### Bottom-left-aligned example -```html title="Top-right" -
- -
-
-

Popover heading

-

Message about this particular piece of UI.

- -
+```html title="Bottom-left" +
+
+

Popover heading

+

Message about this particular piece of UI.

+
``` diff --git a/modules/primer-popover/lib/popover.scss b/modules/primer-popover/lib/popover.scss index 228aff7590..f732ec7f11 100644 --- a/modules/primer-popover/lib/popover.scss +++ b/modules/primer-popover/lib/popover.scss @@ -108,7 +108,7 @@ } &::before { - margin-top: -($spacer-2 + 1); + margin-top: -9px; } &::after { @@ -164,11 +164,11 @@ } &::before { - bottom: $spacer-3; + bottom: $spacer-4; } &::after { - bottom: $spacer-3 + 1; + bottom: 21px; } } diff --git a/modules/primer-support/docs/spacing.md b/modules/primer-support/docs/spacing.md index b1a65e661d..bd18ea2cdd 100644 --- a/modules/primer-support/docs/spacing.md +++ b/modules/primer-support/docs/spacing.md @@ -11,13 +11,13 @@ The spacing scale is a **base-8** scale. We chose a base-8 scale because eight i | Variable | Scale | Value | | --- | --- | --- | -| $spacer-0 | 0 | 0 | +| $spacer-1 | 0 | 0 | | $spacer-1 | 1 | 4px | -| $spacer-2 | 2 | 8px | -| $spacer-3 | 3 | 16px | -| $spacer-4 | 4 | 24px | -| $spacer-5 | 5 | 32px | -| $spacer-6 | 6 | 40px | +| $spacer-1 | 2 | 8px | +| $spacer-1 | 3 | 16px | +| $spacer-1 | 4 | 24px | +| $spacer-1 | 5 | 32px | +| $spacer-1 | 6 | 40px | These variables are encouraged to be used within components and custom CSS. The spacing scale is also used for [margin](./utilities/margin) and [padding](./utilities/padding) utilities. diff --git a/package.json b/package.json index 713921605c..dbe934ba12 100644 --- a/package.json +++ b/package.json @@ -20,21 +20,16 @@ "test": "npm run test-all-modules && lerna run test", "test-all-modules": "ava --verbose tests/test-*.js" }, - "dependencies": { - "primer-module-build": "file:tools/primer-module-build", - "stylelint-config-primer": "file:tools/stylelint-config-primer" - }, "devDependencies": { "@storybook/addon-options": "^3.2.6", "@storybook/react": "^3.2.12", "ava": "^0.23.0", - "babel-core": "^6.26.3", "babel-preset-env": "^1.6.0", "babel-preset-minify": "^0.2.0", "babel-preset-react": "^6.24.1", "commit-status": "^4.1.0", "css-loader": "^0.28.4", - "execa": "^0.10.0", + "execa": "^0.8.0", "fs-extra": "^4.0.2", "gh-pages": "^1.0.0", "glob": "^7.1.2", @@ -43,13 +38,12 @@ "isomorphic-fetch": "^2.2.1", "lerna": "2.4.0", "lerna-changelog": "^0.7.0", - "minimatch": "^3.0.4", - "node-sass": "^4.9.0", + "node-sass": "^4.5.3", "npm-run-all": "^4.0.2", - "npx": "^10.2.0", "octicons": "^6.0.1", "parse-pairs": "^0.2.2", "postcss-loader": "^2.0.6", + "primer-module-build": "^1.0.2", "raw-loader": "^0.5.1", "react": "^15.6.1", "react-dom": "^15.6.1", @@ -59,6 +53,7 @@ "semver": "^5.3.0", "style-loader": "^0.18.2", "stylelint": "^7.13.0", + "stylelint-config-primer": "^2.0.0", "unist-util-find-before": "^2.0.1", "unist-util-parents": "^1.0.0", "unist-util-select": "^1.5.0", diff --git a/script/lint-scss b/script/lint-scss index 26d198cdbd..d9b51599a7 100755 --- a/script/lint-scss +++ b/script/lint-scss @@ -1,3 +1,3 @@ #!/bin/bash set -e -npx stylelint --quiet --syntax scss **/*.scss +$(dirname $0)/npm-run stylelint --quiet --syntax scss **/*.scss diff --git a/script/npm-run-all b/script/npm-run-all index 68c572617c..2ea1f5cac7 100755 --- a/script/npm-run-all +++ b/script/npm-run-all @@ -1,3 +1,3 @@ #!/bin/bash set -e -npx npm-run-all --serial --silent "$@" +$(dirname $0)/npm-run npm-run-all --serial --silent "$@" diff --git a/script/test-docs b/script/test-docs index 3536891259..81c18e4624 100755 --- a/script/test-docs +++ b/script/test-docs @@ -1,3 +1,3 @@ #!/bin/bash set -e -npx ava --verbose $(dirname $0)/../tests/modules/test-document-styles.js "$@" \ No newline at end of file +$(dirname $0)/npm-run ava --verbose $(dirname $0)/../tests/modules/test-document-styles.js diff --git a/tests/modules/test-document-styles.js b/tests/modules/test-document-styles.js index 530e1b7345..89dc436736 100644 --- a/tests/modules/test-document-styles.js +++ b/tests/modules/test-document-styles.js @@ -1,95 +1,73 @@ -const {join} = require('path') -const fse = require('fs-extra') -const globby = require('globby') -const test = require('ava') -const minimatch = require('minimatch') +const test = require("ava") +const css = require(process.cwd()) +const fs = require("fs") +const glob = require("glob") -const cwd = process.cwd() -const css = require(cwd) -const pkg = require(join(cwd, 'package.json')) - -const unique = list => Array.from(new Set(list)).sort() +let selectors +let classnames -/* - * These are the regular expressions that match what we - * expect to be class name instances in the docs. - * Patterns should group the matched class name(s) such that: - * - * ```js - * const [, klass, ] = pattern.exec(content) - * ``` - */ const classPatterns = [ // HTML class attributes /class="([^"]+)"/ig, - /:class ?=> "([^"]+)"/g, - /class: "([^"]+)"/g, // assume that ERB helpers generate an element with the same class /<%= (\w+)\b/g, ] -const whitelistClasses = (pkg.primer ? pkg.primer.class_whitelist || [] : []) - .concat('js*') - -const isWhitelisted = klass => { - return whitelistClasses.some(glob => minimatch(klass, glob)) -} - // Find unique selectors from the cssstats selector list -function uniqueSelectors(selectors) { - return unique(selectors.map(s => { +function uniqueSelectors(s) { + s = s.map(s => { // split multi-selectors into last class used .foo .bar .baz - return s.split(' ').pop() + return s.split(" ").pop() }) .filter(s => { - // only match exact class selectors + // remove any selector that aren't just regular classnames eg. ::hover [type] return s.match(/^\.[a-z\-_]+$/ig) - })) + }) + + // return only the unique selectors + return [...new Set(s)] } // From the given glob sources array, read the files and return found classnames -function getDocumentedClassnames(sources) { - return globby(sources) - .then(paths => { - return Promise.all(paths.map(path => { - return fse.readFile(path, 'utf8') - .then(content => ({path, content})) - })) - }) - .then(files => { - return files.reduce((classes, {path, content}) => { - classPatterns.forEach(pattern => { - let match - while (match = pattern.exec(content)) { - // get the matched classnames and split by whitespace into classes - let klasses = match[1].trim().split(/\s+/) - classes.push(...klasses) - } - }) - return classes - }, []) +function documentedClassnames(sources) { + const classes = [] + const files = sources.reduce((acc, pattern) => { + return acc.concat(glob.sync(pattern)) + }, []) + + files.forEach(file => { + let match = null + const content = fs.readFileSync(file, "utf8") + + classPatterns.forEach(pattern => { + // match each pattern against the source + while (match = pattern.exec(content)) { + // get the matched classnames and split by whitespace into classes + const klasses = match[1].trim().split(/\s+/) + classes.push(...klasses) + } }) - .then(classes => unique(classes)) + }) + + // return only the unique classnames + return Array.from(new Set(classes)) } -const selectors = uniqueSelectors(css.cssstats.selectors.values) -let classnames +// Before all the tests get the selectors and classnames test.before(t => { - return getDocumentedClassnames([ - 'docs/*.md', - 'README.md' - ]) - .then(_ => (classnames = _)) + selectors = uniqueSelectors(css.cssstats.selectors.values) + classnames = documentedClassnames([ + "docs/*.md", + "README.md" + ]) }) -selectors.forEach(selector => { - const klass = selector.replace(/^\./, '') - test(`Selector "${selector}" is documented/whitelisted`, t => { - t.plan(1) - if (isWhitelisted(klass)) { - t.pass(`Selector "${selector}" is whitelisted`) - } else { - t.is(classnames.includes(klass), true, `Selector "${selector}" is not documented`) +test("Every selector class is documented", t => { + const undocumented = [] + selectors.forEach(selector => { + if (!classnames.includes(selector.replace(/^\./, ""))) { + undocumented.push(selector) } }) + t.is(undocumented.length, 0, `I did not find documentation for the "${undocumented.join(", ")}" selector(s) in the ${process.env.npm_package_name} module.`); }) diff --git a/tools/primer-module-build/.eslintrc.json b/tools/primer-module-build/.eslintrc.json deleted file mode 100644 index 70969eb730..0000000000 --- a/tools/primer-module-build/.eslintrc.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "plugins": [ - "eslint-plugin-github" - ], - "env": { - "es6": true, - "node": true - }, - "extends": [ - "plugin:github/recommended", - "plugin:github/es6" - ] -} - diff --git a/tools/primer-module-build/LICENSE b/tools/primer-module-build/LICENSE deleted file mode 100644 index 71fbac5e24..0000000000 --- a/tools/primer-module-build/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 GitHub Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/tools/primer-module-build/cli.js b/tools/primer-module-build/cli.js deleted file mode 100755 index 4ca5ae73dc..0000000000 --- a/tools/primer-module-build/cli.js +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env node -'use strict' -/* eslint-disable no-console */ -const meow = require('meow') -const build = require('./') - -const cli = meow(` - Usage - $ primer-module-build [file] - - File - File. This is required. The file input is the .scss file that - will be built into .css. The build automatically looks in the - node_modules/ directory for any inputs. - - Example - $ primer-module-build index.scss -`) - -build(cli) - .then(files => { - console.warn('YES! wrote %d files:', files.length) - for (const file of files) console.warn(file) - process.exit(0) - }) - .catch(err => { - console.error('NO:', err) - process.exit(1) - }) diff --git a/tools/primer-module-build/index.js b/tools/primer-module-build/index.js deleted file mode 100644 index f4d11ea580..0000000000 --- a/tools/primer-module-build/index.js +++ /dev/null @@ -1,19 +0,0 @@ -const build = require('./lib/build') - -function InputException(message) { - this.message = message - this.name = "InputException" -} - -module.exports = ({input, flags}) => { - if (!input || input.length === 0) { - throw new InputException("You must supply a file to build") - } - - const [file] = input - if (!file.match(/\.scss$/)) { - throw new InputException("We are only able to handle .scss files") - } - - return build(file, flags) -} diff --git a/tools/primer-module-build/lib/.postcss.json b/tools/primer-module-build/lib/.postcss.json deleted file mode 100644 index 0d882f47f2..0000000000 --- a/tools/primer-module-build/lib/.postcss.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "use": ["autoprefixer"], - "autoprefixer": { - "browsers": "> 5%, last 2 firefox versions, last 2 chrome versions, last 2 safari versions, last 2 edge versions, ie 11" - } -} diff --git a/tools/primer-module-build/lib/build.js b/tools/primer-module-build/lib/build.js deleted file mode 100644 index 0ce9c2e358..0000000000 --- a/tools/primer-module-build/lib/build.js +++ /dev/null @@ -1,69 +0,0 @@ -const {isAbsolute} = require('path') -const cssstats = require('cssstats') -const fse = require('fs-extra') -const nodeSassImport = require('node-sass-import') -const postcss = require('postcss') -const promisify = require('pify') -const sass = require('node-sass') - -const sassRender = promisify(sass.render) - -function arrayify(value) { - return Array.isArray(value) ? value : [value] -} - -module.exports = (src, flags = {}) => { - - const cwd = process.cwd() - const sourceFile = isAbsolute(src) ? src : `${cwd}/${src}` - const outputDir = flags.outputDir || `${cwd}/build` - const outputFile = flags.output || `${outputDir}/build.css` - const outputJSFile = flags.outputJS || `${outputDir}/index.js` - const outputDataFile = flags.outputData || `${outputDir}/data.json` - - const sassOptions = { - file: sourceFile, - outputStyle: flags.outputStyle || 'compressed', - importer: nodeSassImport, - includePaths: arrayify(flags.include) - } - - // console.warn('sass options:', sassOptions) - - return fse.mkdirp(outputDir) - .then(() => sassRender(sassOptions)) - .then(({css}) => { - function postcssPlugins() { - const postcssrc = (() => { - if (flags.postcssconfig && fse.existsSync(`${cwd}/${flags.postcssconfig}`)) { - return require(`${cwd}/${flags.postcssconfig}`) - } else if (fse.existsSync(`${cwd}/.postcss.json`)) { - return require(`${cwd}/.postcss.json`) - } else { - return require('./.postcss.json') - } - })() - return postcssrc.use.map(name => { - return require(name)(postcssrc[name]) - }) - } - return postcss(postcssPlugins()) - .process(css, { - from: sourceFile, - to: outputFile - }) - }) - .then(result => { - const data = { - cssstats: cssstats(result.css) - } - const json = JSON.stringify(data) - - return Promise.all([ - fse.writeFile(outputFile, result.css), - fse.writeFile(outputJSFile, `module.exports = ${json}`), - fse.writeFile(outputDataFile, json) - ]) - .then(() => [outputFile, outputJSFile, outputDataFile]) - }) -} diff --git a/tools/primer-module-build/package.json b/tools/primer-module-build/package.json deleted file mode 100644 index a3a527b579..0000000000 --- a/tools/primer-module-build/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "version": "1.0.2", - "name": "primer-module-build", - "description": "Scripts to build primer SCSS modules into CSS and stats", - "homepage": "http://primercss.io/", - "author": "GitHub, Inc.", - "license": "MIT", - "files": [ - "index.js", - "cli.js", - "lib" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/primer/primer-module-build.git" - }, - "bugs": { - "url": "https://github.com/primer/primer-module-build/issues" - }, - "bin": "cli.js", - "engines": { - "node": ">=4" - }, - "scripts": { - "ava": "../../script/npm-run ava --verbose \"tests/**/*.js\"", - "lint": "../../script/npm-run eslint lib/**/*.js *.js tests/**/*.js", - "test": "../../script/npm-run-all lint ava" - }, - "keywords": [ - "primer", - "build", - "css", - "postcss", - "node-sass" - ], - "dependencies": { - "autoprefixer": "^6.7.7", - "cssstats": "^3.2.0", - "meow": "^3.7.0", - "node-sass": "^4.9.0", - "node-sass-import": "^2.0.0", - "pify": "^3.0.0", - "postcss": "^5.2.5" - }, - "devDependencies": { - "primer-utilities": "4.9.0", - "tempy": "^0.2.1" - } -} diff --git a/tools/primer-module-build/tests/fixtures/primer-package.scss b/tools/primer-module-build/tests/fixtures/primer-package.scss deleted file mode 100644 index 32667305d8..0000000000 --- a/tools/primer-module-build/tests/fixtures/primer-package.scss +++ /dev/null @@ -1 +0,0 @@ -@import "primer-utilities/index.scss"; diff --git a/tools/primer-module-build/tests/fixtures/relative.scss b/tools/primer-module-build/tests/fixtures/relative.scss deleted file mode 100644 index 51e575bd19..0000000000 --- a/tools/primer-module-build/tests/fixtures/relative.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./primer-package.scss" diff --git a/tools/primer-module-build/tests/index.js b/tools/primer-module-build/tests/index.js deleted file mode 100644 index 3c48dca0a0..0000000000 --- a/tools/primer-module-build/tests/index.js +++ /dev/null @@ -1,33 +0,0 @@ -const build = require('../lib/build.js') -const test = require('ava') -const tempy = require('tempy') -const {exists} = require('fs-extra') -const {join} = require('path') - -function fixture(...path) { - return join(__dirname, 'fixtures', ...path) -} - -function assertExists(t, filename) { - return exists(filename) - .then(exists => { - exists ? t.pass() : t.fail(`No such file: ${filename}`) - }) -} - -test('resolves npm-installed primer package', t => { - return build(fixture('primer-package.scss'), {}) - .then(() => t.pass()) -}) - -test('resolves relative paths', t => { - return build(fixture('relative.scss'), {}) - .then(() => t.pass()) -}) - -test('writes build.css by default', t => { - const outputDir = tempy.directory() - const outputFile = join(outputDir, 'build.css') - return build(fixture('primer-package.scss'), {outputDir}) - .then(() => assertExists(t, outputFile)) -})