Skip to content
Permalink
Browse files

Doorhanger list view updates (#64)

* logins API, squashed, first version

* Change onCreated to onAdded

* Remove Mozilla-GUID type from schema and replace refs to GUIDs with refs to plain IDs

* Add touch() convenience method

* It's nsILoginMetaInfo, not nsILoginMetaData

* WIP: so close, got nothin

When webdriver opens the extension page, the browser.experiments.logins
API is not present. When I open the extension page myself, via going to
about:debugging, and doing
  browser.tabs.create({
    url: browser.extension.getURL(
           "/test/integration/test-pages/logins-api.html")
  }),
the test page seems just fine. Maybe webdriver needs some magic powers?

* Set the legacy enabled pref when testing, so the API experiments will be available

* fix typo in touch method

* Fill in happy path tests

* Please the linter

* correctly test for update results

* Final tweaks to update() API method

* The webextensions framework auto-inserts 'null' default values for any
specified keys that are missing, causing updates to break
LoginManager.modifyLogin() type checking. Filter out those null values.

* Create a coding convention to make clearer which things are vanilla JS
objects with login fields, which are nsILoginInfo objects, and which are
nsIPropertyBags used to modifyLogin.

* Be sure to wait for async test shutdown before heading onto the next test

* Initial minimal logins API connection to datastore

Connected to #21

* Revise event/message flow in datastore to rely on updates from Login API to apply changes

* Doorhanger list view updates

- fixes #12

* style cleanups for popup list

* more style tweaks to list

* remove refs to verbose and oncopy in panel

* final style nits, and info icon update
  • Loading branch information...
meandavejustice committed Feb 4, 2019
1 parent 3440b21 commit e26cdfc33955103649cb1e7b643e1022333a1d19

Large diffs are not rendered by default.

@@ -1,3 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#B1B1B3" fill-rule="evenodd" d="M2.34314575,13.6568542 C-0.781048583,10.5326599 -0.781048583,5.46734008 2.34314575,2.34314575 C5.46734008,-0.781048583 10.5326599,-0.781048583 13.6568542,2.34314575 C16.7810486,5.46734008 16.7810486,10.5326599 13.6568542,13.6568542 C10.5326599,16.7810486 5.46734008,16.7810486 2.34314575,13.6568542 Z M11.7712362,10.6937401 L9.07749605,8 L11.7712362,5.30625988 C12.0687785,5.00871756 12.0687785,4.52630615 11.7712362,4.22876383 C11.4736938,3.93122152 10.9912824,3.93122152 10.6937401,4.22876383 L8,6.92250395 L5.30625988,4.22876383 C5.00871756,3.93122152 4.52630615,3.93122152 4.22876383,4.22876383 C3.93122152,4.52630615 3.93122152,5.00871756 4.22876383,5.30625988 L6.92250395,8 L4.22876383,10.6937401 C3.93122152,10.9912824 3.93122152,11.4736938 4.22876383,11.7712362 C4.52630615,12.0687785 5.00871756,12.0687785 5.30625988,11.7712362 L8,9.07749605 L10.6937401,11.7712362 C10.9912824,12.0687785 11.4736938,12.0687785 11.7712362,11.7712362 C12.0687785,11.4736938 12.0687785,10.9912824 11.7712362,10.6937401 Z"/>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<defs>
<path id="a" d="M9.061 8l3.47-3.47a.75.75 0 0 0-1.061-1.06L8 6.939 4.53 3.47a.75.75 0 1 0-1.06 1.06L6.939 8 3.47 11.47a.75.75 0 1 0 1.06 1.06L8 9.061l3.47 3.47a.75.75 0 0 0 1.06-1.061L9.061 8z"/>
</defs>
<g fill="none" fill-rule="evenodd" opacity=".8">
<path d="M0 0h16v16H0z"/>
<mask id="b" fill="#fff">
<use xlink:href="#a"/>
</mask>
<use fill="#000" fill-rule="nonzero" xlink:href="#a"/>
<g fill="#0C0C0D" mask="url(#b)">
<path d="M0 0h16v16H0z"/>
</g>
</g>
</svg>
@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<defs>
<path id="a" d="M8.868 1.514c.002-.007.284-.3.845-.883.838-.855.874-.828 1.684 0 .81.828.799.883 0 1.69l-.846.844-1.683-1.651zm-.84.753l1.693 1.708-5.897 5.93-1.7-1.68 5.905-5.958zM1.282 9.045l1.691 1.675c-1.859.932-2.844 1.344-2.955 1.234-.11-.11.31-1.08 1.264-2.909z"/>
</defs>
<g fill="none" fill-rule="evenodd" transform="translate(2 2)">
<path d="M0 0h12v12H0z"/>
<mask id="b" fill="#fff">
<use xlink:href="#a"/>
</mask>
<use fill="#737373" xlink:href="#a"/>
<g fill="#4A4A4F" mask="url(#b)">
<path d="M0 0h12v12H0z"/>
</g>
</g>
</svg>
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<path fill="#38383D" fill-rule="evenodd" d="M10,18 C14.418278,18 18,14.418278 18,10 C18,5.581722 14.418278,2 10,2 C5.581722,2 2,5.581722 2,10 C2,14.418278 5.581722,18 10,18 Z M10,20 C15.523,20 20,15.523 20,10 C20,4.477 15.523,0 10,0 C4.477,0 0,4.477 0,10 C0,15.523 4.477,20 10,20 Z M11,6 C11,6.55228475 10.5522847,7 10,7 C9.44771525,7 9,6.55228475 9,6 C9,5.44771525 9.44771525,5 10,5 C10.5522847,5 11,5.44771525 11,6 Z M10,9 C9.44771525,9 9,9.44771525 9,10 L9,14 C9,14.5522847 9.44771525,15 10,15 C10.5522847,15 11,14.5522847 11,14 L11,10 C11,9.44771525 10.5522847,9 10,9 Z"/>
</svg>
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<path fill="#B1B1B3" fill-rule="evenodd" d="M10,18 C14.418278,18 18,14.418278 18,10 C18,5.581722 14.418278,2 10,2 C5.581722,2 2,5.581722 2,10 C2,14.418278 5.581722,18 10,18 Z M10,20 C15.523,20 20,15.523 20,10 C20,4.477 15.523,0 10,0 C4.477,0 0,4.477 0,10 C0,15.523 4.477,20 10,20 Z M11,6 C11,6.55228475 10.5522847,7 10,7 C9.44771525,7 9,6.55228475 9,6 C9,5.44771525 9.44771525,5 10,5 C10.5522847,5 11,5.44771525 11,6 Z M10,9 C9.44771525,9 9,9.44771525 9,10 L9,14 C9,14.5522847 9.44771525,15 10,15 C10.5522847,15 11,14.5522847 11,14 L11,10 C11,9.44771525 10.5522847,9 10,9 Z"/>
</svg>
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#4A4A4F" fill-opacity=".9" d="M15.758773,14.3442769 L10.9234032,9.50890701 C12.737938,6.95888863 12.2955018,3.44377439 9.90564952,1.42294873 C7.51579721,-0.597876924 3.97603357,-0.45005508 1.76298925,1.76298925 C-0.45005508,3.97603357 -0.597876924,7.51579721 1.42294873,9.90564952 C3.44377439,12.2955018 6.95888863,12.737938 9.50890701,10.9234032 L14.3442769,15.758773 C14.7423023,16.0996333 15.3356221,16.0767159 15.706169,15.706169 C16.0767159,15.3356221 16.0996333,14.7423023 15.758773,14.3442769 Z M6.01781012,10.0405971 C3.8016267,10.0405971 2.00505507,8.24402543 2.00505507,6.02784201 C2.00505507,3.81165859 3.8016267,2.01508696 6.01781012,2.01508696 C8.23399354,2.01508696 10.0305652,3.81165859 10.0305652,6.02784201 C10.0305652,7.09209082 9.60779398,8.11275077 8.85525643,8.86528832 C8.10271888,9.61782587 7.08205893,10.0405971 6.01781012,10.0405971 Z"/>
</svg>
@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#0C0C0D" fill-opacity=".4" d="M15.758773,14.3442769 L10.9234032,9.50890701 C12.737938,6.95888863 12.2955018,3.44377439 9.90564952,1.42294873 C7.51579721,-0.597876924 3.97603357,-0.45005508 1.76298925,1.76298925 C-0.45005508,3.97603357 -0.597876924,7.51579721 1.42294873,9.90564952 C3.44377439,12.2955018 6.95888863,12.737938 9.50890701,10.9234032 L14.3442769,15.758773 C14.7423023,16.0996333 15.3356221,16.0767159 15.706169,15.706169 C16.0767159,15.3356221 16.0996333,14.7423023 15.758773,14.3442769 Z M6.01781012,10.0405971 C3.8016267,10.0405971 2.00505507,8.24402543 2.00505507,6.02784201 C2.00505507,3.81165859 3.8016267,2.01508696 6.01781012,2.01508696 C8.23399354,2.01508696 10.0305652,3.81165859 10.0305652,6.02784201 C10.0305652,7.09209082 9.60779398,8.11275077 8.85525643,8.86528832 C8.10271888,9.61782587 7.08205893,10.0405971 6.01781012,10.0405971 Z"/>
<path fill="#b1b1b3" fill-opacity=".9" d="M15.758773,14.3442769 L10.9234032,9.50890701 C12.737938,6.95888863 12.2955018,3.44377439 9.90564952,1.42294873 C7.51579721,-0.597876924 3.97603357,-0.45005508 1.76298925,1.76298925 C-0.45005508,3.97603357 -0.597876924,7.51579721 1.42294873,9.90564952 C3.44377439,12.2955018 6.95888863,12.737938 9.50890701,10.9234032 L14.3442769,15.758773 C14.7423023,16.0996333 15.3356221,16.0767159 15.706169,15.706169 C16.0767159,15.3356221 16.0996333,14.7423023 15.758773,14.3442769 Z M6.01781012,10.0405971 C3.8016267,10.0405971 2.00505507,8.24402543 2.00505507,6.02784201 C2.00505507,3.81165859 3.8016267,2.01508696 6.01781012,2.01508696 C8.23399354,2.01508696 10.0305652,3.81165859 10.0305652,6.02784201 C10.0305652,7.09209082 9.60779398,8.11275077 8.85525643,8.86528832 C8.10271888,9.61782587 7.08205893,10.0405971 6.01781012,10.0405971 Z"/>
</svg>
@@ -8,7 +8,7 @@
padding: 40px;
overflow: hidden;
overflow-wrap: break-word;
color: #4a4a4f;
color: #737373;
font-size: 15px;
line-height: 1.5;
letter-spacing: 0.2px;
@@ -25,17 +25,18 @@
}

.item .item-summary:hover {
background-color: rgba(10, 132, 255, 0.07);
background-color: #ededf0;
}

.item .item-summary:active {
background-color: rgba(10, 132, 255, 0.12);
background-color: #e7e7e7;
}

.item[data-selected] .item-summary {
position: relative;
background-color: rgba(10, 132, 255, 0.12);
box-shadow: 0 -1px 0 rgba(10, 132, 255, 0.4), 0 1px 0 rgba(10, 132, 255, 0.4);
background-color: #e7e7e7;
box-shadow: 0 -1px 0 #e7e7e7, 0 1px 0 #e7e7e7;
cursor: pointer;
}

/* Provide borders at the top and bottom of selected items. These are ::before
@@ -69,8 +70,3 @@
.item-list:focus .item[data-selected] .item-summary::after {
background-color: #0a84ff;
}

.verbose + .verbose {
margin-top: 4px;
border-top: 1px solid #d7d7db;
}
@@ -11,17 +11,16 @@ import ScrollingList from "../../widgets/scrolling-list";

import styles from "./item-list.css";

export default function ItemList({items, itemClassName, verbose, onCopy,
...props}) {
export default function ItemList({items, itemClassName, panel, ...props}) {
return (
<ScrollingList {...props} className={"itemList " + styles.itemList}
itemClassName={classNames([
styles.item, verbose && styles.verbose, itemClassName,
styles.item, itemClassName,
])} data={items} styledItems={false}>
{({id, title, username}) => {
return (
<ItemSummary className={styles.itemSummary} id={id} title={title}
username={username} verbose={verbose} onCopy={onCopy}/>
username={username} panel={panel}/>
);
}}
</ScrollingList>
@@ -37,13 +36,11 @@ ItemList.propTypes = {
}).isRequired
).isRequired,
itemClassName: PropTypes.string,
verbose: PropTypes.bool,
onCopy: PropTypes.func,
panel: PropTypes.bool,
};

ItemList.defaultProps = {
itemClassName: "",
verbose: false,
};

export function ItemListPlaceholder({children}) {
@@ -2,22 +2,37 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

.item-summary-container::before {
content: "";
float: right;
.item-summary-container {
cursor: pointer;
}

.item-summary {
padding: 9px 15px;
position: relative;
width: 5px;
height: 9px;
margin: 32px 20px;
background-image: url(/icons/chevron-right.svg);
}

.info {
position: absolute;
right: 0px;
top: -3px;
width: 18px;
height: 18px;
margin: 20px;
margin-right: 12px;
background-image: url(/icons/info.svg);
background-repeat: no-repeat;
background-size: 5px 9px;
z-index: 2;
background-size: 18px 18px;
background-position: 50%;
padding: 7px;
border-radius: 2px;
}

.item-summary {
padding: 16px 20px;
font-size: 15px;
.info:hover {
background-color: rgba(12, 12, 13, 0.1);
}

.item-summary:hover .info {
background-image: url(/icons/info-dark.svg);
}

.title,
@@ -30,40 +45,13 @@
}

.title {
font-weight: bold;
font-size: 15px;
font-weight: 600;
color: #0c0c0d;
}

.subtitle {
font-size: 13px;
font-weight: 400;
}

.copy-buttons {
border-top: 1px solid #d7d7db;
display: flex;
}

.copy-button {
flex: 1;
height: 40px;
}

.copy-button + .copy-button {
border-inline-start: 1px solid #d7d7db;
}

.copy-button-inner {
border-radius: 0;
}

.copy-button-inner:focus {
box-shadow: inset 0 0 0 2px #0a84ff;
}

.copy-button-inner:hover {
background-color: #ededf0;
}

.copy-button-inner:active:hover {
background-color: #d7d7db;
color: #737373;
}
@@ -8,54 +8,10 @@ import React from "react";

import { classNames } from "../../common";
import { NEW_ITEM_ID } from "../common";
import CopyToClipboardButton from "../../widgets/copy-to-clipboard-button";

import styles from "./item-summary.css";

function ItemSummaryCopyButtons({id, username, onCopy}) {
async function getPassword() {
const response = await browser.runtime.sendMessage({
type: "get_item",
id,
});
return response.item.entry.password;
}

return (
<div className={styles.copyButtons}
onMouseDown={(e) => e.stopPropagation()}>
<Localized id="item-summary-copy-username" attrs={{title: true}}>
<CopyToClipboardButton className={styles.copyButton}
buttonClassName={styles.copyButtonInner}
value={username}
onCopy={toCopy => onCopy("username", toCopy)}>
cOPy uSERNAMe
</CopyToClipboardButton>
</Localized>
<Localized id="item-summary-copy-password" attrs={{title: true}}>
<CopyToClipboardButton className={styles.copyButton}
buttonClassName={styles.copyButtonInner}
value={getPassword}
onCopy={toCopy => onCopy("password", toCopy)}>
cOPy pASSWORd
</CopyToClipboardButton>
</Localized>
</div>
);
}

ItemSummaryCopyButtons.propTypes = {
id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
onCopy: PropTypes.func.isRequired,
};

export default function ItemSummary({className, id, title, username, verbose,
onCopy}) {
// istanbul ignore next
if (id === NEW_ITEM_ID && verbose) {
throw new Error("verbose <ItemSummary/> cannot be used with new items");
}
export default function ItemSummary({className, id, title, username, panel}) {

const trimmedTitle = title.trim();
const trimmedUsername = username.trim();
@@ -74,9 +30,8 @@ export default function ItemSummary({className, id, title, username, verbose,
$length={trimmedUsername.length}>
<div data-name="subtitle" className={styles.subtitle}>no uSERNAMe</div>
</Localized>
{panel && <span className={styles.info}></span>}
</div>
{verbose && <ItemSummaryCopyButtons id={id} username={username}
onCopy={onCopy}/>}
</div>
);
}
@@ -86,14 +41,13 @@ ItemSummary.propTypes = {
id: PropTypes.string,
title: PropTypes.string,
username: PropTypes.string,
verbose: PropTypes.bool,
onCopy: PropTypes.func,
panel: PropTypes.bool,
};

ItemSummary.defaultProps = {
className: "",
id: null,
title: "",
username: "",
verbose: false,
showInfo: false,
};
@@ -16,7 +16,7 @@ function ItemFilter({inputRef, ...props}) {
"aria-label": true, "placeholder": true,
}}>
<FilterInput {...props} aria-label="fILTER…" placeholder="fILTEr…"
ref={inputRef}/>
ref={inputRef} />
</Localized>
);
}
@@ -5,10 +5,10 @@
body {
padding: 0;
margin: 0;
width: 280px;
height: 360px;
width: 300px;
height: 339px;
color: #0c0c0d;
background-color: #ededf0;
background-color: #ffffff;
font: caption;
font-size: 13px;
-moz-user-select: none;
@@ -4,4 +4,22 @@

.panel-body {
padding: 1em;
background: #F9F9FA;
}

.panel-header {
background: #fff;
text-align: center;
}

.panel-header .edit-btn {
display: block;
background-image: url(/icons/edit.svg);
background-repeat: no-repeat;
background-size: 16px 16px;
height: 16px;
width: 16px;
position: absolute;
top: 16px;
right: 8px;
}
@@ -6,23 +6,40 @@ import { Localized } from "fluent-react";
import React from "react";
import PropTypes from "prop-types";

import Panel, { PanelHeader, PanelBody } from "../../../widgets/panel";
import Panel, { PanelHeader, PanelBody, PanelFooter,
PanelFooterButton } from "../../../widgets/panel";
import { ItemFields } from "../../components/item-fields";

import styles from "./item-details-panel.css";

export default function ItemDetailsPanel({fields, onCopy, onBack}) {
const openWebsite = (url) => {
browser.runtime.sendMessage({
type: "open_site",
url,
});
window.close();
};

return (
<Panel>
<Localized id="item-details-panel-title">
<PanelHeader onBack={onBack}>
eNTRy dETAILs
<PanelHeader onBack={onBack} className={styles.panelHeader}>
lOGIn dETAILs
</PanelHeader>
</Localized>

<PanelBody className={styles.panelBody}>
<ItemFields fields={fields} onCopy={onCopy}/>
</PanelBody>

<PanelFooter border="floating">
<Localized id="list-detail-button">
<PanelFooterButton onClick={openWebsite} className={styles.panelFooterButton}>
oPEn wEBSITe
</PanelFooterButton>
</Localized>
</PanelFooter>
</Panel>
);
}

0 comments on commit e26cdfc

Please sign in to comment.
You can’t perform that action at this time.