Skip to content

Commit

Permalink
Refactor Passkey Details Touchpoint
Browse files Browse the repository at this point in the history
- Update Passkey Details Card to handle data as an Object
- Parse Dates and OS from each activity
- Code Refactor
- Update Storybook files
  • Loading branch information
kevin-villalobos-trusona committed Oct 17, 2023
1 parent da95b14 commit 46c4beb
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 103 deletions.
15 changes: 12 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"web-types-lit": "^0.1.0"
},
"dependencies": {
"lit": "^2.8.0"
"lit": "^2.8.0",
"moment": "^2.29.4"
},
"ts-standard": {
"ignore": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Meta, StoryObj} from '@storybook/web-components'
import {html} from 'lit'

import './fido-passkey-details-card'
import {PasskeyDetails} from "./fido-passkey-details-card";

export default {
title: 'Fido Passkey Details Card',
Expand Down Expand Up @@ -29,24 +30,30 @@ export default {
}
} as Meta


let passkeyDetails: PasskeyDetails = {
createdAt: '2023-10-12T17:12:13.183221Z',
createdOperatingSystem: 'Mac OS',
passkeyActivity: [
{
lastUsedAt: '2023-10-13T17:12:13.183221Z',
operatingSystem: 'Android 14'
},
{
lastUsedAt: '2023-10-14T17:12:13.183221Z',
operatingSystem: 'Android 14'
}
]
}

export const Default: StoryObj = {
name: 'Default',
args:{
savedText: "Saved with iOS 16.2 on April 11, 2023, 12:01am",
lastUsedText:"iOS 16.2, April 11, 2023, 3:42pm",
lastUsedIsMobile: true,
prevLastUsedText:"MacOS 13.0.1, April 23rd, 2023, 4:21pm",
prevLastUsedIsMobile:false
args: {
passkeyDetails: passkeyDetails
},
render: (args) => {
return html`
<fido-passkey-details-card
savedText="${args.savedText}"
lastUsedText="${args.lastUsedText}"
lastUsedIsMobile="${args.lastUsedIsMobile}"
prevLastUsedText="${args.prevLastUsedText}"
prevLastUsedIsMobile="${args.prevLastUsedIsMobile}"
>
<fido-passkey-details-card passkeyDetails="${JSON.stringify(args.passkeyDetails)}">
</fido-passkey-details-card>
`
}
Expand Down
115 changes: 77 additions & 38 deletions src/components/fido-passkey-details-card/fido-passkey-details-card.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { LitElement, html, css, TemplateResult } from 'lit'
import { customElement, property } from 'lit/decorators.js'
import { sharedStyles } from '../../shared/style'
import {LitElement, html, css, TemplateResult} from 'lit'
import {customElement, property} from 'lit/decorators.js'
import {repeat} from 'lit/directives/repeat.js'
import {sharedStyles} from '../../shared/style'
import moment from "moment";

const componentStyle = css`
Expand Down Expand Up @@ -82,20 +84,27 @@ const componentStyle = css`
`

export interface PasskeyActivity {
lastUsedAt: string
operatingSystem: string
}

export interface PasskeyDetails {
createdAt: string
createdOperatingSystem: string
passkeyActivity: Array<PasskeyActivity>
}

@customElement('fido-passkey-details-card')
class FidoPasskeyDetailsCard extends LitElement {
@property({ type: String }) savedText?: string

@property({ type: String }) lastUsedText: string = ''
@property({ type: String }) lastUsedIsMobile: string = 'false'
@property({type: Object}) passkeyDetails?: PasskeyDetails

@property({ type: String }) prevLastUsedText: string | null = null
@property({ type: String }) prevLastUsedIsMobile: string = 'false'
static styles = [sharedStyles, componentStyle]

static styles = [sharedStyles, componentStyle]

render (): TemplateResult {
return html`
render(): TemplateResult {
console.log(this.passkeyDetails)
return html`
<div class="auth-card">
<div class="auth-card-header">
<svg class="auth-img" viewBox="0 0 22 21" fill="none" xmlns="http://www.w3.org/2000/svg">
Expand All @@ -106,7 +115,7 @@ class FidoPasskeyDetailsCard extends LitElement {
<path d="M12.8722 11.9558C12.0414 11.6077 11.1331 11.4072 10.1694 11.4072H6.84635C3.17989 11.4072 0.200195 14.245 0.200195 17.7369V19.8468H14.6002V14.034C13.847 13.496 13.2599 12.7787 12.8722 11.9663V11.9558Z"
fill="#19064E"/>
</svg>
<p class="auth-card-h2">${this.savedText}</p>
<p class="auth-card-h2">${this.getCreatedAtText()}</p>
</div>
<div class="auth-card-body">
<div class="auth-card-title">
Expand All @@ -120,25 +129,63 @@ class FidoPasskeyDetailsCard extends LitElement {
stroke="#444444" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
${this.getRow(this.lastUsedText, this.lastUsedIsMobile === 'true')}
${this.getOptionalRow()}
${this.getLastUsedActivity()}
</div>
</div>
`
}
}

private getRow (lastUsedText: string, lastUsedIsMobile: boolean): TemplateResult {
return html`
private getCreatedAtText(): string {
const createdAt = this.passkeyDetails?.createdAt
const os = this.passkeyDetails?.createdOperatingSystem
const parsedDate = moment(createdAt ?? "")
if (parsedDate.isValid()) {
const formattedDate = parsedDate.format("MMMM D, YYYY, h:mma")
return `Saved with ${os ?? "--"} on ${formattedDate}`
} else {
return ""
}
}

private getLastUsedAtText(lastUsedAt?: string, lastUsedOs?: String): string {
const parsedDate = moment(lastUsedAt ?? "")
const formattedDate = parsedDate.format("MMMM D, YYYY, h:mma")
return `${lastUsedOs ?? ""}, ${formattedDate}`
}

private getLastUsedActivity(): TemplateResult {
if (this.passkeyDetails?.passkeyActivity === null || this.passkeyDetails?.passkeyActivity === undefined || this.passkeyDetails?.passkeyActivity.length == 0) {
return html`
${this.getActivityRow({
lastUsedAt: this.passkeyDetails?.createdAt ?? "",
operatingSystem: this.passkeyDetails?.createdOperatingSystem ?? ""
})}
`
} else {
return html`
${repeat(this.passkeyDetails?.passkeyActivity ?? [], (activity: PasskeyActivity) => html`
${this.getActivityRow(activity)}
`)}`
}
}


private isMobile(operatingSystem?: string) {
return operatingSystem?.toLowerCase().includes('android') || operatingSystem?.toLowerCase().includes('ios')
}

private getActivityRow(activity: PasskeyActivity): TemplateResult {
return html`
<div class="auth-card-row">
${this.getIcon(lastUsedIsMobile)}
<p class="auth-card-h3">${lastUsedText}</p>
${this.getIcon(this.isMobile(activity.operatingSystem) ?? false)}
<p class="auth-card-h3">${this.getLastUsedAtText(activity.lastUsedAt, activity.operatingSystem)}</p>
</div>
`
}
}

private getIcon (isMobile: boolean): TemplateResult {
if (isMobile) {
return html`
private getIcon(isMobile: boolean): TemplateResult {
if (isMobile) {
return html`
<svg class="passkey-type-img" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6114 11.6851H4.38916" stroke="#444444" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.5003 1.7959H5.50027C4.88662 1.7959 4.38916 2.29336 4.38916 2.90701V13.4626C4.38916 14.0762 4.88662 14.5737 5.50027 14.5737H10.5003C11.1139 14.5737 11.6114 14.0762 11.6114 13.4626V2.90701C11.6114 2.29336 11.1139 1.7959 10.5003 1.7959Z"
Expand All @@ -147,8 +194,8 @@ class FidoPasskeyDetailsCard extends LitElement {
fill="#444444"/>
</svg>
`
} else {
return html`
} else {
return html`
<svg class="passkey-type-img" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5534_164)">
<path d="M14.4995 9.68506V3.18506C14.4995 2.35173 13.8262 1.68506 12.9995 1.68506H2.99951C2.16618 1.68506 1.49951 2.35173 1.49951 3.18506V9.68506C1.49951 9.95839 1.71951 10.1851 1.99951 10.1851H13.9995C14.2728 10.1851 14.4995 9.95839 14.4995 9.68506ZM13.4995 9.68506L13.9995 9.18506H1.99951L2.49951 9.68506V3.18506C2.49951 2.90506 2.71951 2.68506 2.99951 2.68506H12.9995C13.2728 2.68506 13.4995 2.90506 13.4995 3.18506V9.68506Z"
Expand All @@ -165,20 +212,12 @@ class FidoPasskeyDetailsCard extends LitElement {
</defs>
</svg>
`
}
}
}

private getOptionalRow (): TemplateResult {
if (this.prevLastUsedText && this.prevLastUsedText !== '') {
return this.getRow(this.prevLastUsedText, this.prevLastUsedIsMobile === 'true')
} else {
return html``
}
}
}

declare global {
interface HTMLElementTagNameMap {
'fido-passkey-details-card': FidoPasskeyDetailsCard
}
interface HTMLElementTagNameMap {
'fido-passkey-details-card': FidoPasskeyDetailsCard
}
}
41 changes: 29 additions & 12 deletions src/fido/fido-passkey-details/fido-passkey-details.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,42 @@ export default {
}
} as Meta

let passkeyDetails : PasskeyDetails[] = [
let passkeyDetails: PasskeyDetails[] = [
{
savedText: "Saved with iOS 16.2 on April 11, 2023, 12:01am",
lastUsedText:"iOS 16.2, April 11, 2023, 3:42pm",
lastUsedIsMobile: true,
prevLastUsedText:"MacOS 13.0.1, April 23rd, 2023, 4:21pm",
prevLastUsedIsMobile:false
createdAt: '2023-10-12T17:12:13.183221Z',
createdOperatingSystem: 'Mac OS',
passkeyActivity: [
{
lastUsedAt: '2023-10-13T17:12:13.183221Z',
operatingSystem: 'Android 14'
},
{
lastUsedAt: '2023-10-14T17:12:13.183221Z',
operatingSystem: 'Android 14'
}
]
}, {
createdAt: '2023-10-10T17:12:13.183221Z',
createdOperatingSystem: 'Android 13'
},
{
savedText: "Saved with Android 13 on April 11, 2023, 12:01am",
lastUsedText:"Android 13, April 11th, 2023, 3:42pm",
lastUsedIsMobile: true
},
passkeyActivity: [
{
lastUsedAt: '2023-10-03T17:10:13.183221Z',
operatingSystem: 'Android 10'
},
{
lastUsedAt: '2023-10-01T17:14:13.183221Z',
operatingSystem: 'Android 10'
}
]
}
]

export const Default: StoryObj = {
name: 'Default',
args:{
passkeyDetails : passkeyDetails
args: {
passkeyDetails: passkeyDetails
},
render: (args) => {
return html`
Expand Down
49 changes: 18 additions & 31 deletions src/fido/fido-passkey-details/fido-passkey-details.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { LitElement, html, css, TemplateResult } from 'lit'
import { repeat } from 'lit/directives/repeat.js'
import { customElement, property } from 'lit/decorators.js'
import { sharedStyles } from '../../shared/style'
import {LitElement, html, css, TemplateResult} from 'lit'
import {repeat} from 'lit/directives/repeat.js'
import {customElement, property} from 'lit/decorators.js'
import {sharedStyles} from '../../shared/style'
import {PasskeyDetails} from "../../components/fido-passkey-details-card/fido-passkey-details-card";

const componentStyle = css`
Expand Down Expand Up @@ -54,22 +55,14 @@ const componentStyle = css`
`

export default interface PasskeyDetails {
savedText: string
lastUsedText: string
lastUsedIsMobile: boolean
prevLastUsedText?: string | null
prevLastUsedIsMobile?: boolean | null
}

@customElement('fido-passkey-details')
class FidoPasskeyDetails extends LitElement {
@property({ type: Array }) passkeyDetails: PasskeyDetails[] = new Array<PasskeyDetails>()
@property({type: Array}) passkeyDetails: PasskeyDetails[] = new Array<PasskeyDetails>()

static styles = [sharedStyles, componentStyle]
static styles = [sharedStyles, componentStyle]

render (): TemplateResult {
return html`
render(): TemplateResult {
return html`
<div class="auth-container">
<p class="auth-h1">Passkeys</p>
${this.getPasskeys()}
Expand All @@ -92,26 +85,20 @@ class FidoPasskeyDetails extends LitElement {
</div>
</div>
`
}
}

private getPasskeys (): TemplateResult {
return html`
${repeat(this.passkeyDetails, (passkeyDetail: PasskeyDetails) => html`
<fido-passkey-details-card
savedText="${passkeyDetail.savedText}"
lastUsedText="${passkeyDetail.lastUsedText}"
lastUsedIsMobile="${passkeyDetail.lastUsedIsMobile}"
prevLastUsedText="${passkeyDetail.prevLastUsedText}"
prevLastUsedIsMobile="${passkeyDetail.prevLastUsedIsMobile}"
>
private getPasskeys(): TemplateResult {
return html`
${repeat(this.passkeyDetails, (details: PasskeyDetails) => html`
<fido-passkey-details-card passkeyDetails="${JSON.stringify(details)}">
</fido-passkey-details-card>
`)}
`
}
}
}

declare global {
interface HTMLElementTagNameMap {
'fido-passkey-details': FidoPasskeyDetails
}
interface HTMLElementTagNameMap {
'fido-passkey-details': FidoPasskeyDetails
}
}
Loading

0 comments on commit 46c4beb

Please sign in to comment.