~alcinnz/haskell-stylist

3bd6ae52f8c93b8c85c7b6a18e921fde7336c8e5 — Adrian Cochrane 5 years ago af343c1
Test/fix cascade logic for selector specificity.
3 files changed, 36 insertions(+), 6 deletions(-)

M src/Data/CSS/Style.hs
M src/Data/CSS/Style/Importance.hs
M test/Test.hs
M src/Data/CSS/Style.hs => src/Data/CSS/Style.hs +19 -5
@@ 1,7 1,9 @@
module Data.CSS.Style(
        QueryableStyleSheet(..), queryableStyleSheet,
        queryRules,
        PropertyParser(..), cascade
        PropertyParser(..), cascade,
        TrivialPropertyParser(..),
        Element(..), Attribute(..)
    ) where

import Data.CSS.Style.Selector.Index


@@ 15,6 17,7 @@ import Data.CSS.Syntax.StyleSheet (StyleSheet(..))
--      or is another Map implementation better?
import Data.HashMap.Strict
import Data.CSS.Syntax.Tokens
import Data.Text (unpack)

type QueryableStyleSheet parser = QueryableStyleSheet' (ImportanceSplitter (
        PropertyExpander parser (OrderedRuleStore (InterpretedRuleStore StyleIndex))


@@ 40,9 43,9 @@ queryRules (QueryableStyleSheet' store _ _) el = lookupRules store el
---- Cascade
--------

cascadeRules rules = cascadeProperties $ concat $ Prelude.map properties rules
cascadeRules overrides rules = cascadeProperties overrides $ concat $ Prelude.map properties rules

cascadeProperties ((name, value):props) = insert name value $ cascadeProperties props
cascadeProperties overrides props = fromList (props ++ overrides)

--------
---- Dispatch to property definitions


@@ 56,12 59,13 @@ class PropertyParser a where
    -- longhand parent self name value
    longhand :: a -> a -> Text -> [Token] -> Maybe a

cascade :: PropertyParser p => QueryableStyleSheet p -> Element -> p -> p
cascade self el parent = dispatch parent parent $ toList $ cascadeRules $ queryRules self el
cascade :: PropertyParser p => QueryableStyleSheet p -> Element -> [(Text, [Token])] -> p -> p
cascade self el overrides parent = dispatch parent parent $ toList $ cascadeRules overrides $ queryRules self el

dispatch parent child ((name, value):props)
    | Just child' <- longhand parent child name value = dispatch parent child' props
    | otherwise = dispatch parent child props
dispatch _ child [] = child

--- Verify syntax during parsing, so invalid properties don't interfere with cascade.
data PropertyExpander parser inner = PropertyExpander parser inner


@@ 76,3 80,13 @@ expandRule parser rule = rule {inner = StyleRule selector $ expandProperties par
expandProperties parser ((name, value):props) =
        shorthand parser name value ++ expandProperties parser props
expandProperties _ [] = []

--------
---- Testing utility
--------

data TrivialPropertyParser = TrivialPropertyParser (HashMap String [Token])
instance PropertyParser TrivialPropertyParser where
    temp = TrivialPropertyParser empty
    longhand _ (TrivialPropertyParser self) key value =
        Just $ TrivialPropertyParser $ insert (unpack key) value self

M src/Data/CSS/Style/Importance.hs => src/Data/CSS/Style/Importance.hs +1 -0
@@ 13,6 13,7 @@ splitProperties (prop@(name, value):rest)
            (unimportant, (name, reverse value'):important)
        | otherwise = (prop:unimportant, important)
    where (unimportant, important) = splitProperties rest
splitProperties [] = ([], [])

data ImportanceSplitter a = ImportanceSplitter a
instance RuleStore inner => RuleStore (ImportanceSplitter inner) where

M test/Test.hs => test/Test.hs +16 -1
@@ 3,8 3,9 @@ module Main where

import Test.Hspec
import Test.Hspec.QuickCheck
import Data.CSS.Syntax.Tokens
import Data.HashMap.Strict

import Data.CSS.Syntax.Tokens
import Data.CSS.Syntax.StyleSheet
import Data.CSS.Syntax.Selector



@@ 212,8 213,22 @@ spec = do
            selector sibling `shouldBe` False
            selector child `shouldBe` True

    describe "Style resolution" $ do
        it "respects selector specificity" $ do
            let el = ElementNode {
                name = "a",
                parent = Nothing,
                previous = Nothing,
                attributes = [Attribute "class" "link"]
            }
            let rules = parse queryable "a.link {color: green} a {color: red}"
            let TrivialPropertyParser style = cascade rules el [] temp::TrivialPropertyParser
            style ! "color" `shouldBe` [Ident "green"]

styleIndex :: StyleIndex
styleIndex = new
queryable :: QueryableStyleSheet TrivialPropertyParser
queryable = queryableStyleSheet
emptyStyle = TrivialStyleSheet []
linkStyle = TrivialStyleSheet [sampleRule]
sampleRule = StyleRule (Element [Tag "a"]) [("color", [Ident "green"])]