diff --git a/README.md b/README.md index a625b375bbdaf8389b71cb601d673d2fe3e737d9..452b6a9ea2a3f31779b904376b93793dc283e128 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ -This component is currently a work in progress, right now it does work as a stand alone component and in tandem with the myuw-app-bar component. - -Right now all of the data is hardcoded in as a placeholder, this will be updated soon to use dynamic data pulled in from the MyUW session endpoint. +#myuw-profile ## Getting Started Add the following import to your page's `<head>`: ```html -<link rel="import" href="https://unpkg.com/@myuw-web-components/myuw-profile@1.0.0/myuw-profile.html"> +<link rel="import" href="https://cdn.rawgit.com/myuw-web-components/myuw-profile/0eb2e944/myuw-profile.html"> ``` Use the component's HTML tag wherever you want: @@ -17,6 +15,7 @@ Use the component's HTML tag wherever you want: login-url="" logout-url="" session-endpoint="" + background-color="" open-right > </myuw-profile> @@ -27,6 +26,7 @@ Use the component's HTML tag wherever you want: - **Login URL (login-url):** The URL to redirect users to on login - **Logout URL (logout-url):** The Logout URL to redirect users to on logout - **Session Endpoint (session-endpoint):** The endpoint URL for session info +- **Background color (background-color):** Use this to dynamically set the background color of the profile menu button - **Open Menu Right (open-right):** Include this attribute if you would like the profile menu to open to the right, instead of left #### Slots @@ -36,6 +36,7 @@ Use the component's HTML tag wherever you want: #### CSS Variables - `--myuw-profile-font`: Set the font stack for this component -- `--myuw-profile-login-color`: Se the font color for the "Login" button +- `--myuw-profile-login-color`: Set the font color of the "Login" button +- `--myuw-profile-background-color`: Set the background color of the circular menu button For more information about CSS variables and how they work with MyUW Web Components, [reference the styles component](https://github.com/myuw-web-components/myuw-app-styles "reference the styles component") \ No newline at end of file diff --git a/index.html b/index.html index 830c3f94f852b519fd3ba59b82be91ba2cd3a37b..8e6bfca35c8e3d95e3a6c121ff6557944d06102c 100644 --- a/index.html +++ b/index.html @@ -4,32 +4,126 @@ body { padding: 0; margin: 0; + background: #ccc; + font-family: Arial, Helvetica, sans-serif; + } + .demo-content { + padding: 0 36px; + } + .demo-item { + height: 48px; + display: flex; + justify-content: start; + align-content: center; + align-items: flex-end; + flex-direction: row; + } + .demo-item .column { + display: flex; + flex-direction: column; + } + .demo-item label { + font-size: 14px; + margin-bottom: 4px; + } + .demo-item input { + height: 36px; + border-radius: 5px; + margin-right: 8px; + font-size: 14px; + + border: none; + } + .demo-item button { + min-width: 48px; + min-height: 24px; + padding: 8px 8px; + font-size: 14px; + overflow-y: hidden; + border-radius: 5px; + transition: background 0.15s ease-in-out; + } + .demo-item button:hover { + background: #ebebeb; + cursor: pointer; } </style> - <script src="https://unpkg.com/@myuw-web-components/myuw-app-styles/myuw-app-styles.js"></script> - <link rel="import" href="https://unpkg.com/@myuw-web-components/myuw-app-bar@1.0.0/myuw-app-bar.html"> + <script src="https://cdn.rawgit.com/myuw-web-components/myuw-app-styles/6f62858b/myuw-app-styles.js"></script> + <link rel="import" href="https://cdn.rawgit.com/myuw-web-components/myuw-app-bar/86b5c15b/myuw-app-bar.html"> <link rel="import" href="myuw-profile.html"> </head> <body> - + <!-- App bar with profile button --> <myuw-app-bar theme-name="MyUW" theme-url="" - app-name="Component Test" + app-name="Profile Demo" app-url="" background="rgb(197, 5, 12)" - color="white" - > + color="white"> <myuw-profile slot="myuw-profile" login-url="https://wisc.edu/" logout-url="https://wisc.edu/" - session-endpoint="./session.json" - > - <a href="https://wisc.edu" slot="nav-item">UW Madison Home</a> - <a href="https://wisc.edu" slot="nav-item">STAR</a> + session-endpoint="./session.json"> + <a href="#" slot="nav-item">UW Madison Home</a> + <a href="#" slot="nav-item">STAR</a> </myuw-profile> </myuw-app-bar> + + <!-- Demo content --> + <div class="demo-content"> + <h1>Demo</h1> + <div class="demo-item"> + <div class="column"> + <label for="newColor">Color: </label> + <input id="newColor" type="text" placeholder="#888"> + </div> + <button aria-label="set profile button color" onclick="setColor(newColor.value)">Set profile color</button> + </div> + <div class="demo-item"> + <button aria-label="set no session" onclick="setSession('jargon')">No session</button> + </div> + <div class="demo-item"> + <button aria-label="restore demo session" onclick="setSession('./session.json')">Restore demo session</button> + </div> + <div class="demo-item"> + <button aria-label="set missing session-endpoint" onclick="setSession(null)">Set missing session endpoint</button> + </div> + </div> + + <!-- Functions for demo content --> + <script> + function setColor(newColor) { + if (newColor.indexOf('#') < 0) { + newColor = '#' + newColor; + } + document.getElementsByTagName('myuw-profile')[0].setAttribute('background-color', newColor); + } + + function setSession(session) { + // Remove profile from DOM + document.getElementsByTagName('myuw-profile')[0].remove(); + // Construct new profile template + var newProfileTemplate = document.createElement('myuw-profile'); + newProfileTemplate.setAttribute('slot', 'myuw-profile'); + newProfileTemplate.setAttribute('login-url', 'https://wisc.edu/'); + newProfileTemplate.setAttribute('logout-url', 'https://wisc.edu/'); + newProfileTemplate.setAttribute('color', '#fb686d'); + newProfileTemplate.innerHTML = ` + <a href="https://wisc.edu" slot="nav-item">UW Madison Home</a> + <a href="https://wisc.edu" slot="nav-item">STAR</a> + `; + if (session) { + newProfileTemplate.setAttribute('session-endpoint', session); + } + // Reinsert into DOM + document.getElementsByTagName('myuw-app-bar')[0].appendChild(newProfileTemplate); + } + </script> + + <!-- Accessibility tester --> + <script src="./node_modules/tota11y/build/tota11y.min.js"></script> </body> </html> \ No newline at end of file diff --git a/myuw-profile.html b/myuw-profile.html index d62b99a0b449748d1ab5f9e0bc604edcac54ced9..6a9178ac88f33cdae853f25a2f127b3ec4e77a10 100644 --- a/myuw-profile.html +++ b/myuw-profile.html @@ -16,6 +16,7 @@ font-size: 14px; position: relative; display: inline-block; + transition: background .25s cubic-bezier(0.0, 0.0, 0.2, 1); } #myuw-profile-login.hidden { @@ -23,15 +24,25 @@ } #myuw-profile-circle { - position: relative; display: inline-block; - width: 40px; - height: 40px; - border-radius: 100%; - display: flex; - justify-content: center; - align-items: center; + outline: none; + border-radius: 50%; + height: 42px; + width: 42px; + min-width: initial; + margin-left: 8px; + padding: 4px; + text-transform: uppercase; + text-align: center; + background: transparent; + border-color: transparent; + transition: background .25s cubic-bezier(0.0, 0.0, 0.2, 1); + } + + #myuw-profile-circle:hover, + #myuw-profile-login:hover { cursor: pointer; + background: rgba(0,0,0,0.12); } #myuw-profile-circle-initial { @@ -40,9 +51,20 @@ font-weight: 500; font-size: 18px; font-family: var( --myuw-profile-font, var(--myuw-font, 'Montserrat', 'Roboto', Arial, sans-serif) ); + background-color: var( --myuw-profile-background-color, #fb686d); user-select: none; color: white; text-transform: capitalize; + border-radius: 50%; + display: block; + margin: 0; + overflow: hidden; + position: relative; + height: 33px; + width: 33px; + line-height: 33px; + font-size: 18px; + font-weight: 400; } #myuw-profile-wrapper { @@ -64,15 +86,17 @@ margin: 0; padding: 0; font-size: 14px; + z-index: 101; transform-origin: top right; transform: scale(0); opacity: 0; - transition: opacity .25s cubic-bezier(0.0, 0.0, 0.2, 1), transform .25s cubic-bezier(0.0, 0.0, 0.2, 1); + visibility: hidden; + transition: visibility 0s, opacity .25s cubic-bezier(0.0, 0.0, 0.2, 1), transform .25s cubic-bezier(0.0, 0.0, 0.2, 1); - -webkit-box-shadow: 0px 0px 7px 0px rgba(50, 50, 50, 0.75); - -moz-box-shadow: 0px 0px 7px 0px rgba(50, 50, 50, 0.75); - box-shadow: 0px 0px 7px 0px rgba(50, 50, 50, 0.75); + -webkit-box-shadow: 0 2px 4px -1px rgba(0,0,0,0.2), 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12); + -moz-box-shadow: 0 2px 4px -1px rgba(0,0,0,0.2), 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12); + box-shadow: 0 2px 4px -1px rgba(0,0,0,0.2), 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12); } #myuw-profile-nav.open-right { @@ -84,6 +108,7 @@ #myuw-profile-nav.visible { transform: scale(1); opacity: 1; + visibility: visible; } #myuw-profile-nav p { @@ -94,55 +119,81 @@ #myuw-profile-nav a, #myuw-profile-nav p, ::slotted(a), - ::slotted(p){ + ::slotted(p) { transition: all .3s ease; position: relative; font-size: 15px; font-family: var( --myuw-profile-font, var(--myuw-font, 'Montserrat', 'Roboto', Arial, sans-serif) ); - display: block; - padding: 14px 16px; - color: black; + padding: 3px 16px; + color: rgba(0,0,0,0.87); text-decoration: none; - background-color: #f9f9f9; - border-bottom: solid 1px #e5e5e5; + background-color: #f5f5f5; + border-bottom: 1px solid #e5e5e5; user-select: none; + outline: none; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + flex-direction: row; + min-height: 48px; + height: 48px; + -webkit-align-content: center; + align-content: center; + -webkit-align-items: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; } - #myuw-profile-nav a:first-child, - #myuw-profile-nav p:first-child { - padding: 14px 16px; - font-weight: bold; - background-color: white; + #myuw-profile-nav #myuw-profile-nav-user { + font-weight: 600; + text-transform: capitalize; + background-color: rgb(255,255,255); + border-bottom: none; } - #myuw-profile-nav a:first-child:hover, - #myuw-profile-nav p:first-child:hover { - background-color: white; + #myuw-profile-nav #myuw-profile-nav-user:hover { + background-color: rgb(255,255,255); } #myuw-profile-nav a:hover, + #myuw-profile-nav a:focus, #myuw-profile-nav p:hover, + #myuw-profile-nav p:focus, ::slotted(a:hover), - ::slotted(p:hover) { - background-color: rgba(158,158,158,0.2); + ::slotted(a:focus), + ::slotted(p:hover), + ::slotted(p:focus) { + background-color: #ececec; } </style> <a href="#" id="myuw-profile-login" class="hidden">Login</a> <div id="myuw-profile-wrapper" class="hidden"> - <div id="myuw-profile-circle"> - <p id="myuw-profile-circle-initial">B</p> - </div> - - <div id="myuw-profile-nav"> - <p id="myuw-profile-nav-user">Bucky</p> + <button id="myuw-profile-circle" + aria-label="profile menu" + aria-haspopup="true" + aria-controls="myuw-profile-nav" + aria-expanded="false"> + <p id="myuw-profile-circle-initial">B</p> + </button> + + <ul id="myuw-profile-nav" + role="menu" + tabindex="-1" + aria-labelledby="myuw-profile-circle"> + <p id="myuw-profile-nav-user"></p> + <li role="menuitem"> <slot name="nav-item"></slot> - <a href="#">Settings</a> + </li> + <li role="menuitem"> <a id="myuw-profile-logout" href="#">Logout</a> - </div> + </li> + </ul> </div> - </template> <script> class MyUWProfile extends HTMLElement { @@ -158,7 +209,14 @@ class MyUWProfile extends HTMLElement { this.shadowRoot.appendChild(MyUWProfile.template.content.cloneNode(true)); } - static get observedAttributes() {return ['login-url', 'logout-url', 'open-right']; } + static get observedAttributes() { + return [ + 'login-url', + 'logout-url', + 'open-right', + 'background-color' + ]; + } attributeChangedCallback(name, oldValue, newValue){ // Update the attribute internally @@ -173,14 +231,15 @@ class MyUWProfile extends HTMLElement { this['login-url'] = this.getAttribute('login-url'); this['logout-url'] = this.getAttribute('logout-url'); this['session-endpoint'] = this.getAttribute('session-endpoint'); + this['background-color'] = this.getAttribute('background-color'); this['user'] = false; - if(this.getAttribute('open-right') !== null){ + if (this.getAttribute('open-right') !== null) { this['open-right'] = true; } // If the session endpoint is set, fetch session info - if(this['session-endpoint']){ + if (this['session-endpoint']) { fetch(this['session-endpoint']) .then(res => { @@ -217,8 +276,9 @@ class MyUWProfile extends HTMLElement { window.addEventListener('click', e => { let nav = this.shadowRoot.getElementById('myuw-profile-nav'); - if(nav.classList.contains('visible')){ - nav.classList.remove('visible') + if (nav.classList.contains('visible')) { + nav.classList.remove('visible'); + this.shadowRoot.getElementById('myuw-profile-circle').setAttribute('aria-expanded', 'false'); } }); @@ -242,8 +302,21 @@ class MyUWProfile extends HTMLElement { and the menu will never open. */ this.shadowRoot.getElementById('myuw-profile-circle').addEventListener('click', e => { + // Find menu button and the first nav list item + let nav = this.shadowRoot.getElementById('myuw-profile-nav'); + let menuButton = this.shadowRoot.getElementById('myuw-profile-circle'); + e.stopPropagation(); - this.shadowRoot.getElementById('myuw-profile-nav').classList.toggle('visible'); + nav.classList.toggle('visible'); + + // Focus the menu upon opening, blur on close + if (nav.classList.contains('visible')) { + nav.focus(); + menuButton.setAttribute('aria-expanded', 'true'); + } else { + nav.blur(); + menuButton.setAttribute('aria-expanded', 'false'); + } }); // Update the component to use the new attributes @@ -251,10 +324,11 @@ class MyUWProfile extends HTMLElement { this.updateAttribute('logout-url'); this.updateAttribute('session-endpoint'); this.updateAttribute('open-right'); + this.updateAttribute('background-color'); } // Update the component with attribute values - updateAttribute(att){ + updateAttribute(att) { switch(att){ case 'login-url': this.shadowRoot.getElementById('myuw-profile-login').setAttribute('href', this['login-url']); @@ -269,6 +343,9 @@ class MyUWProfile extends HTMLElement { this.shadowRoot.getElementById('myuw-profile-nav').classList.add('open-right'); } break; + case 'background-color': + this.shadowRoot.getElementById('myuw-profile-circle-initial').style.backgroundColor = this['background-color']; + break; } } @@ -283,14 +360,12 @@ class MyUWProfile extends HTMLElement { If not, the login button will show. */ componentReady() { - if(this.user){ + if (this.user) { // Add user's name to first menu item this.shadowRoot.getElementById('myuw-profile-nav-user').innerHTML = this.user.firstName; // Change the letter in the profile circle this.shadowRoot.getElementById('myuw-profile-circle-initial').innerHTML = this.user.firstName.substring(0,1); - - this.setProfileColor(); - + // Show the profile bubble this.showProfileBubble(); } else { if (this['login-url'] !== null) { @@ -301,29 +376,6 @@ class MyUWProfile extends HTMLElement { } } - /* - Programmatically select a profile background - color based on the length of the users first name - and the first letter of their first name - */ - setProfileColor() { - const colors = [ - '#093145', - '#107896', - '#829356', - '#00A996', - '#BCA136', - '#744196' - ]; - - const firstName = this.user.firstName.length; - const initial = this.user.firstName.substring(0,1).charCodeAt(); - const colorIndex = (initial + firstName) % colors.length; - - this.shadowRoot.getElementById('myuw-profile-circle').style.backgroundColor = colors[colorIndex]; - - } - showLoginButton() { // Show Login Button this.shadowRoot.getElementById('myuw-profile-login').classList.remove('hidden'); diff --git a/package-lock.json b/package-lock.json index b41c8825957e4bd739f59532ef87ddd63119178e..62ea1a4718ac5645ad35cc285f69ac95c1ba2125 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1199,6 +1199,12 @@ "integrity": "sha1-lYYL/MXHbCd/j4Mm/Q9bLiDrohc=", "dev": true }, + "tota11y": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/tota11y/-/tota11y-0.1.6.tgz", + "integrity": "sha1-MScjYU/5X/u4dOVogElAd0ceFUc=", + "dev": true + }, "toxic": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toxic/-/toxic-1.0.0.tgz", diff --git a/package.json b/package.json index e4b90ce1efb51865b62413ffe44af955d83a41e9..d669f946dd5375e3bec3ac20d151f1824c34e743 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "homepage": "https://github.com/myuw-web-components/myuw-profile#readme", "devDependencies": { - "superstatic": "^5.0.1" + "superstatic": "^5.0.1", + "tota11y": "^0.1.6" } }